Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 34da4438 authored by Vincent Bourgmayer's avatar Vincent Bourgmayer
Browse files

Merge branch '8183-t-fix-multi-import-FAHIM' into 'v1-t'

feat: import multiple events from .ics file

See merge request !77
parents b54a8add 38921b85
Loading
Loading
Loading
Loading
+148 −3
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 * Copyright (C) 2022 The Calyx Institute
 * Copyright (C) 2024 MURENA SAS
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -23,10 +24,11 @@ import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;

import android.Manifest;
import android.app.AlarmManager;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ObjectAnimator;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.content.AsyncQueryHandler;
import android.content.BroadcastReceiver;
@@ -83,6 +85,8 @@ import com.android.calendar.CalendarController.EventType;
import com.android.calendar.CalendarController.ViewType;
import com.android.calendar.agenda.AgendaFragment;
import com.android.calendar.alerts.AlertService;
import com.android.calendar.event.CalendarPickerDialogFragment;
import com.android.calendar.event.EditEventHelper;
import com.android.calendar.month.MonthByWeekFragment;
import com.android.calendar.selectcalendars.SelectVisibleCalendarsFragment;
import com.android.calendar.settings.GeneralPreferences;
@@ -97,6 +101,7 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
@@ -104,13 +109,15 @@ import java.util.TimeZone;
import ws.xsoh.etar.R;

public class AllInOneActivity extends AbstractCalendarActivity implements EventHandler,
        OnSharedPreferenceChangeListener, SearchView.OnQueryTextListener, SearchView.OnSuggestionListener, NavigationView.OnNavigationItemSelectedListener {
        OnSharedPreferenceChangeListener, SearchView.OnQueryTextListener, SearchView.OnSuggestionListener, NavigationView.OnNavigationItemSelectedListener, CalendarPickerDialogFragment.CalendarPickerDialogListener {
    public static final String BUNDLE_KEY_MULTIPLE_EVENTS = "key_multiple_events";
    private static final String TAG = "AllInOneActivity";
    private static final boolean DEBUG = false;
    private static final String EVENT_INFO_FRAGMENT_TAG = "EventInfoFragment";
    private static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
    private static final String BUNDLE_KEY_EVENT_ID = "key_event_id";
    private static final String BUNDLE_KEY_RESTORE_VIEW = "key_restore_view";
    private static final String BUNDLE_KEY_RESTORE_MULTIPLE_EVENTS = "key_restore_multiple_events";
    private static final int HANDLER_KEY = 0;
    private static final int PERMISSIONS_REQUEST_WRITE_CALENDAR = 0;
    private static final int PERMISSIONS_REQUEST_POST_NOTIFICATIONS = 1;
@@ -234,6 +241,20 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH
    private LinearLayout.LayoutParams mVerticalControlsParams;
    private AllInOneMenuExtensionsInterface mExtensions = ExtensionsFactory
            .getAllInOneMenuExtensions();
    private List<CalendarEventModel> mEventList = Collections.emptyList();

    private void showCalendarPickerDialog() {
        FragmentManager fragmentManager = getSupportFragmentManager();
        CalendarPickerDialogFragment fragment =
                ((CalendarPickerDialogFragment) fragmentManager.findFragmentByTag(CalendarPickerDialogFragment.FRAGMENT_TAG));

        if (fragment != null) {
            fragment.dismiss();
        }

        fragment = new CalendarPickerDialogFragment(mEventList.size());
        fragment.show(fragmentManager, CalendarPickerDialogFragment.FRAGMENT_TAG);
    }

    @Override
    protected void onNewIntent(Intent intent) {
@@ -254,6 +275,15 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH
                mController.sendEvent(this, EventType.GO_TO, time, time, -1, ViewType.CURRENT);
            }
        }
        handleEventsOnNewIntent(intent);
    }

    private void handleEventsOnNewIntent(Intent intent) {
        if (intent.hasExtra(AllInOneActivity.BUNDLE_KEY_MULTIPLE_EVENTS)
                && intent.getExtras().containsKey(BUNDLE_KEY_MULTIPLE_EVENTS)) {
            Bundle bundle = intent.getExtras();
            handleEvents(bundle, intent);
        }
    }

    @Override
@@ -382,6 +412,57 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH
        prefs.registerOnSharedPreferenceChangeListener(this);

        mContentResolver = getContentResolver();

        // Save and restore flow for handling import of multiple events
        if (icicle == null) {
            handleEvents(icicle, getIntent());
        } else {
            mEventList = ((List<CalendarEventModel>) icicle.getSerializable(BUNDLE_KEY_RESTORE_MULTIPLE_EVENTS));
        }
    }

    private void handleEvents(Bundle bundle, Intent intent) {
        mEventList = getEventList(bundle, intent);
        if (mEventList.isEmpty()) {
            return;
        }

        // TODO: 19/08/2024 Decide to show dialog for consecutive .ics import. Now it replaces previous one.
        showCalendarPickerDialog();
    }

    private List<CalendarEventModel> getEventList(Bundle icicle, Intent intent) {
        final List<CalendarEventModel> eventModelList = new ArrayList<>();

        final ArrayList<Bundle> bundles = intent.getParcelableArrayListExtra(AllInOneActivity.BUNDLE_KEY_MULTIPLE_EVENTS);

        if (bundles == null || bundles.isEmpty()) {
            return eventModelList;
        }

        for (Bundle bundle : bundles) {
            long eventId = -1;
            Uri data = intent.getData();
            if (data != null) {
                try {
                    eventId = Long.parseLong(data.getLastPathSegment());
                } catch (NumberFormatException e) {
                    if (DEBUG) {
                        Log.d(TAG, "Create new event");
                    }
                }
            } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) {
                eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID, -1);
            }

            final CalendarEventModel eventModel = new CalendarEventModel(getApplicationContext(), bundle);
            if (eventId > -1) {
                eventModel.mId = eventId;
            }
            eventModelList.add(eventModel);
        }

        return eventModelList;
    }

    private void checkAppPermissions() {
@@ -657,7 +738,6 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH
        mCalIntentReceiver = Utils.setTimeChangesReceiver(this, mTimeChangesUpdater);
    }


    @Override
    protected void onPause() {
        super.onPause();
@@ -698,6 +778,7 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH
        super.onSaveInstanceState(outState);
        outState.putLong(BUNDLE_KEY_RESTORE_TIME, mController.getTime());
        outState.putInt(BUNDLE_KEY_RESTORE_VIEW, mCurrentView);
        outState.putSerializable(BUNDLE_KEY_RESTORE_MULTIPLE_EVENTS, new ArrayList<>(mEventList));
        if (mCurrentView == ViewType.EDIT) {
            outState.putLong(BUNDLE_KEY_EVENT_ID, mController.getEventId());
        } else if (mCurrentView == ViewType.AGENDA) {
@@ -1481,6 +1562,70 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH
        return false;
    }

    @Override
    public void onCalendarPicked(long selectedCalendarId, String ownerAccount, String syncAccountName) {
        saveEventsInPickedCalendar(selectedCalendarId, ownerAccount, syncAccountName);
    }

    @Override
    public void onCalendarUnavailable() {
        showAddCalendarDialog();
    }

    private void showAddCalendarDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.no_syncable_calendars)
                .setIconAttribute(android.R.attr.alertDialogIcon)
                .setMessage(R.string.no_calendars_found)
                .setPositiveButton(R.string.add_calendar, (dialog, which) -> {
                    Intent nextIntent = new Intent(this, SettingsActivity.class);
                    startActivity(nextIntent);
                })
                .setNegativeButton(android.R.string.no, null);
        
        builder.show();
    }

    @Override
    public void onCalendarPickerError() {
        Toast.makeText(this, R.string.pick_calendar_error_importing_events, Toast.LENGTH_SHORT).show();
    }

    private void saveEventsInPickedCalendar(long selectedCalendarId, String ownerAccount, String syncAccountName) {
        final EditEventHelper editEventHelper = new EditEventHelper(this);

        int addedEventcounter = 0;

        for (CalendarEventModel event : mEventList) {
            event.mCalendarId = selectedCalendarId;
            event.mOwnerAccount = ownerAccount;
            event.mSyncAccountName = syncAccountName;

            if (editEventHelper.saveEvent(event, null, 0, null)) {
                addedEventcounter++;
            } else {
                Toast.makeText(
                    this,
                    getString(R.string.import_multi_event_single_event_failure_toast, event.mTitle),
                    Toast.LENGTH_SHORT
                ).show();
            }
        }

        if (addedEventcounter > 0) {
            Toast.makeText(this, getString(R.string.import_multi_event_confirmation_toast), Toast.LENGTH_SHORT).show();
            navigateToFirstEventOccurrence();
        }
    }

    private void navigateToFirstEventOccurrence() {
        CalendarEventModel calendarEventModel = mEventList.get(0);
        long start = calendarEventModel.mStart;
        long end = calendarEventModel.mEnd;
        CalendarController.getInstance(this).launchViewEvent(-1, start, end,
                Attendees.ATTENDEE_STATUS_NONE);
    }

    private class QueryHandler extends AsyncQueryHandler {
        public QueryHandler(ContentResolver cr) {
            super(cr);
+90 −1
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 * Copyright (C) 2024 MURENA SAS
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
@@ -16,9 +17,15 @@

package com.android.calendar;

import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Attendees;
import android.provider.CalendarContract.Calendars;
import android.provider.CalendarContract.Events;
@@ -31,7 +38,6 @@ import androidx.annotation.Nullable;
import com.android.calendar.event.EditEventHelper;
import com.android.calendar.event.EventColorCache;
import com.android.calendar.event.ExtendedProperty;
import com.android.calendar.icalendar.VEvent;
import com.android.calendar.settings.GeneralPreferences;
import com.android.common.Rfc822Validator;

@@ -149,6 +155,89 @@ public class CalendarEventModel implements Serializable {
        }
    }

    public CalendarEventModel(Context context, Bundle bundle) {
        this(context);

        if (bundle == null) {
            return;
        }

        final String title = bundle.getString(Events.TITLE);
        if (title != null) {
            mTitle = title;
        }

        final String location = bundle.getString(Events.EVENT_LOCATION);
        if (location != null) {
            mLocation = location;
        }

        final String description = bundle.getString(Events.DESCRIPTION);
        if (description != null) {
            mDescription = description;
        }

        final String url = bundle.getString(ExtendedProperty.URL);
        if (url != null) {
            mUrl = url;
        }

        final int availability = bundle.getInt(Events.AVAILABILITY, -1);
        if (availability != -1) {
            mAvailability = availability;
            mAvailabilityExplicitlySet = true;
        }

        final int accessLevel = bundle.getInt(Events.ACCESS_LEVEL, -1);
        if (accessLevel != -1) {
            mAccessLevel = accessLevel;
        }

        final String rrule = bundle.getString(Events.RRULE);
        if (!TextUtils.isEmpty(rrule)) {
            mRrule = rrule;
        }

        final String timezone = bundle.getString(Events.EVENT_TIMEZONE);
        if (timezone != null) {
            mTimezone = timezone;
        }

        final long beginTime = bundle.getLong(EXTRA_EVENT_BEGIN_TIME, -1);
        if (beginTime > -1) {
            mStart = beginTime;
        }

        final long endTime = bundle.getLong(EXTRA_EVENT_END_TIME, -1);
        if (endTime > -1) {
            mEnd = endTime;
        }

        final boolean isAllDay = bundle.getBoolean(EXTRA_EVENT_ALL_DAY, false);
        if (isAllDay) {
            mAllDay = isAllDay;
        }

        final String organizer = bundle.getString(CalendarContract.Events.ORGANIZER, "");
        if (!organizer.isEmpty()) {
            mOrganizer = organizer;
        }

        final String emails = bundle.getString(Intent.EXTRA_EMAIL);
        if (!TextUtils.isEmpty(emails)) {
            final String[] emailArray = emails.split("[ ,;]");
            for (String email : emailArray) {
                if (!TextUtils.isEmpty(email) && email.contains("@")) {
                    email = email.trim();
                    if (!mAttendeesList.containsKey(email)) {
                        mAttendeesList.put(email, new Attendee("", email));
                    }
                }
            }
        }
    }


    public CalendarEventModel(Context context, Intent intent) {
        this(context);

+213 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 MURENA SAS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

package com.android.calendar;

import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.NonNull;

import com.android.calendar.event.EditEventActivity;
import com.android.calendar.event.ExtendedProperty;
import com.android.calendar.icalendar.Attendee;
import com.android.calendar.icalendar.IcalendarUtils;
import com.android.calendar.icalendar.VEvent;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.TimeZone;

import ws.xsoh.etar.R;

public class EventUtils {
    private final static String TAG = EventUtils.class.getName();
    private EventUtils() {
    }

    @NonNull
    public static Bundle createBundleFromEvent(VEvent event, Context context) {
        Bundle bundle = new Bundle();

        bundle.putString(CalendarContract.Events.TITLE,
                IcalendarUtils.uncleanseString(event.getProperty(VEvent.SUMMARY)));
        bundle.putString(CalendarContract.Events.EVENT_LOCATION,
                IcalendarUtils.uncleanseString(event.getProperty(VEvent.LOCATION)));
        bundle.putString(CalendarContract.Events.DESCRIPTION,
                IcalendarUtils.uncleanseString(event.getProperty(VEvent.DESCRIPTION)));
        bundle.putString(ExtendedProperty.URL,
                IcalendarUtils.uncleanseString(event.getProperty(VEvent.URL)));
        bundle.putString(CalendarContract.Events.ORGANIZER,
                IcalendarUtils.uncleanseString(event.getProperty(VEvent.ORGANIZER)));
        bundle.putString(CalendarContract.Events.RRULE,
                IcalendarUtils.uncleanseString(event.getProperty(VEvent.RRULE)));

        if (event.mAttendees.size() > 0) {
            StringBuilder builder = new StringBuilder();
            for (Attendee attendee : event.mAttendees) {
                builder.append(attendee.mEmail);
                builder.append(",");
            }
            bundle.putString(Intent.EXTRA_EMAIL, builder.toString());
        }

        String dtStart = event.getProperty(VEvent.DTSTART);
        String dtStartParam = event.getPropertyParameters(VEvent.DTSTART);
        if (!TextUtils.isEmpty(dtStart)) {
            bundle.putLong(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
                    getLocalTimeFromString(dtStart, dtStartParam, context));
        }

        String dtEnd = event.getProperty(VEvent.DTEND);
        String dtEndParam = event.getPropertyParameters(VEvent.DTEND);
        if (dtEnd != null && !TextUtils.isEmpty(dtEnd)) {
            bundle.putLong(CalendarContract.EXTRA_EVENT_END_TIME,
                    getLocalTimeFromString(dtEnd, dtEndParam, context));
        } else {
            // Treat start date as end date if un-specified
            dtEnd = dtStart;
            dtEndParam = dtStartParam;
        }

        boolean isAllDay = getLocalTimeFromString(dtEnd, dtEndParam, context)
                - getLocalTimeFromString(dtStart, dtStartParam, context) == 24*60*60*1000;


        if (isTimeStartOfDay(dtStart, dtStartParam, context)) {
            bundle.putBoolean(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay);
        }
        //Check if some special property which say it is a "All-Day" event.

        String microsoft_all_day_event = event.getProperty("X-MICROSOFT-CDO-ALLDAYEVENT");
        if (!TextUtils.isEmpty(microsoft_all_day_event) && microsoft_all_day_event.equals("TRUE")) {
            bundle.putBoolean(CalendarContract.EXTRA_EVENT_ALL_DAY, true);
        }

        bundle.putBoolean(EditEventActivity.EXTRA_READ_ONLY, true);

        return bundle;
    }

    private static boolean isTimeStartOfDay(String dtStart, String dtStartParam, Context context) {
        // convert to epoch milli seconds
        long timeStamp = getLocalTimeFromString(dtStart, dtStartParam, context);
        Date date = new Date(timeStamp);

        DateFormat dateFormat = new SimpleDateFormat("HH:mm");
        String dateStr = dateFormat.format(date);
        if (dateStr.equals("00:00")) {
            return true;
        }
        return false;
    }

    private static long getLocalTimeFromString(String iCalDate, String iCalDateParam, Context context) {
        // see https://tools.ietf.org/html/rfc5545#section-3.3.5

        // FORM #2: DATE WITH UTC TIME, e.g. 19980119T070000Z
        if (iCalDate.endsWith("Z")) {
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
            format.setTimeZone(TimeZone.getTimeZone("UTC"));

            try {
                format.parse(iCalDate);
                format.setTimeZone(TimeZone.getDefault());
                return format.getCalendar().getTimeInMillis();
            } catch (ParseException exception) {
                Log.e(TAG, "Can't parse iCalDate:", exception);
            }
        }

        // FORM #3: DATE WITH LOCAL TIME AND TIME ZONE REFERENCE, e.g. TZID=America/New_York:19980119T020000
        else if (iCalDateParam != null && iCalDateParam.startsWith("TZID=")) {
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
            String timeZone = iCalDateParam.substring(5).replace("\"", "");
            // This is a pretty hacky workaround to prevent exact parsing of VTimezones.
            // It assumes the TZID to be refered to with one of the names recognizable by Java.
            // (which are quite a lot, see e.g. http://tutorials.jenkov.com/java-date-time/java-util-timezone.html)
            if (Arrays.asList(TimeZone.getAvailableIDs()).contains(timeZone)) {
                format.setTimeZone(TimeZone.getTimeZone(timeZone));
            } else {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    String convertedTimeZoneId = android.icu.util.TimeZone
                            .getIDForWindowsID(timeZone, "001");
                    if (convertedTimeZoneId != null && !convertedTimeZoneId.equals("")) {
                        format.setTimeZone(TimeZone.getTimeZone(convertedTimeZoneId));
                    } else {
                        format.setTimeZone(TimeZone.getDefault());
                        Toast.makeText(
                                context,
                                context.getString(R.string.cal_import_error_time_zone_msg, timeZone),
                                Toast.LENGTH_SHORT).show();
                    }
                } else {
                    format.setTimeZone(TimeZone.getDefault());
                    Toast.makeText(
                            context,
                            context.getString(R.string.cal_import_error_time_zone_msg, timeZone),
                            Toast.LENGTH_SHORT).show();
                }
            }
            try {
                format.parse(iCalDate);
                return format.getCalendar().getTimeInMillis();
            } catch (ParseException exception) {
                Log.e(TAG, "Can't parse iCalDate:", exception);
            }
        }

        // ONLY DATE, e.g. 20190415
        else if (iCalDateParam != null && iCalDateParam.equals("VALUE=DATE")) {
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
            format.setTimeZone(TimeZone.getDefault());

            try {
                format.parse(iCalDate);
                return format.getCalendar().getTimeInMillis();
            } catch (ParseException exception) {
                Log.e(TAG, "Can't parse iCalDate:", exception);
            }
        }

        // FORM #1: DATE WITH LOCAL TIME, e.g. 19980118T230000
        else {
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
            format.setTimeZone(TimeZone.getDefault());

            try {
                format.parse(iCalDate);
                return format.getCalendar().getTimeInMillis();
            } catch (ParseException exception) {
                Log.e(TAG, "Can't parse iCalDate:", exception);
            }
        }

        Toast.makeText(context, context.getString(R.string.cal_import_error_date_msg, iCalDate), Toast.LENGTH_SHORT).show();

        return System.currentTimeMillis();
    }
}
+46 −4
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 MURENA SAS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

package com.android.calendar;

import android.app.Activity;
@@ -25,9 +43,11 @@ import java.io.File;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;

import ws.xsoh.etar.R;
@@ -142,15 +162,37 @@ public class ImportActivity extends Activity {
            return;
        }

        Intent calIntent = new Intent(Intent.ACTION_INSERT);
        calIntent.setType("vnd.android.cursor.item/event");

        LinkedList<VEvent> events = calendar.getAllEvents();
        if (events == null) {
        if (events == null || events.isEmpty()) {
            showErrorToast();
            return;
        }

        if (events.size() == 1){
            handleSingleEvent(calendar);
        } else {
            handleMultipleEvents(events);
        }
    }

    private void handleMultipleEvents(List<VEvent> events) {
        Intent intent = new Intent(this, AllInOneActivity.class);
        ArrayList<Bundle> bundleList = new ArrayList<>();

        for (VEvent event : events) {
            bundleList.add(EventUtils.createBundleFromEvent(event, this));
        }

        intent.putParcelableArrayListExtra(AllInOneActivity.BUNDLE_KEY_MULTIPLE_EVENTS, bundleList);
        startActivity(intent);

        finish();
    }

    private void handleSingleEvent(VCalendar calendar) {
        Intent calIntent = new Intent(Intent.ACTION_INSERT);
        calIntent.setType("vnd.android.cursor.item/event");

        VEvent firstEvent = calendar.getAllEvents().getFirst();
        calIntent.putExtra(CalendarContract.Events.TITLE,
                IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.SUMMARY)));
+65 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading