From 1d4468f32b92fbe6c5110af2ebb53ad1c2b90cbc Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 8 Aug 2024 17:39:22 +0200 Subject: [PATCH 01/45] refactor: reorganize code of 'ImportActivity.parseCalFile()': Move code to fetch all events from VCalendar instance before Intent instantiation --- app/src/main/java/com/android/calendar/ImportActivity.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/android/calendar/ImportActivity.java b/app/src/main/java/com/android/calendar/ImportActivity.java index c55f31c6f..a4dde349f 100644 --- a/app/src/main/java/com/android/calendar/ImportActivity.java +++ b/app/src/main/java/com/android/calendar/ImportActivity.java @@ -142,15 +142,15 @@ public class ImportActivity extends Activity { return; } - Intent calIntent = new Intent(Intent.ACTION_INSERT); - calIntent.setType("vnd.android.cursor.item/event"); - LinkedList events = calendar.getAllEvents(); if (events == null) { showErrorToast(); return; } + 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))); -- GitLab From 5203913a572bd5315bf02f97a1c40a73957813a8 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 8 Aug 2024 17:43:33 +0200 Subject: [PATCH 02/45] feat(ImportActivity.parseCalFile()): only start EditEventActivity if there is a single event in the cal file --- .../com/android/calendar/ImportActivity.java | 118 +++++++++--------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/com/android/calendar/ImportActivity.java b/app/src/main/java/com/android/calendar/ImportActivity.java index a4dde349f..48761dc36 100644 --- a/app/src/main/java/com/android/calendar/ImportActivity.java +++ b/app/src/main/java/com/android/calendar/ImportActivity.java @@ -143,78 +143,80 @@ public class ImportActivity extends Activity { } LinkedList events = calendar.getAllEvents(); - if (events == null) { + if (events == null || events.isEmpty()) { showErrorToast(); return; } - 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))); - calIntent.putExtra(CalendarContract.Events.EVENT_LOCATION, - IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.LOCATION))); - calIntent.putExtra(CalendarContract.Events.DESCRIPTION, - IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.DESCRIPTION))); - calIntent.putExtra(ExtendedProperty.URL, - IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.URL))); - calIntent.putExtra(CalendarContract.Events.ORGANIZER, - IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.ORGANIZER))); - calIntent.putExtra(CalendarContract.Events.RRULE, - IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.RRULE))); - - if (firstEvent.mAttendees.size() > 0) { - StringBuilder builder = new StringBuilder(); - for (Attendee attendee : firstEvent.mAttendees) { - builder.append(attendee.mEmail); - builder.append(","); + if (events.size() == 1) { + 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))); + calIntent.putExtra(CalendarContract.Events.EVENT_LOCATION, + IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.LOCATION))); + calIntent.putExtra(CalendarContract.Events.DESCRIPTION, + IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.DESCRIPTION))); + calIntent.putExtra(ExtendedProperty.URL, + IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.URL))); + calIntent.putExtra(CalendarContract.Events.ORGANIZER, + IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.ORGANIZER))); + calIntent.putExtra(CalendarContract.Events.RRULE, + IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.RRULE))); + + if (firstEvent.mAttendees.size() > 0) { + StringBuilder builder = new StringBuilder(); + for (Attendee attendee : firstEvent.mAttendees) { + builder.append(attendee.mEmail); + builder.append(","); + } + calIntent.putExtra(Intent.EXTRA_EMAIL, builder.toString()); } - calIntent.putExtra(Intent.EXTRA_EMAIL, builder.toString()); - } - String dtStart = firstEvent.getProperty(VEvent.DTSTART); - String dtStartParam = firstEvent.getPropertyParameters(VEvent.DTSTART); - if (!TextUtils.isEmpty(dtStart)) { - calIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, - getLocalTimeFromString(dtStart, dtStartParam)); - } + String dtStart = firstEvent.getProperty(VEvent.DTSTART); + String dtStartParam = firstEvent.getPropertyParameters(VEvent.DTSTART); + if (!TextUtils.isEmpty(dtStart)) { + calIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, + getLocalTimeFromString(dtStart, dtStartParam)); + } - String dtEnd = firstEvent.getProperty(VEvent.DTEND); - String dtEndParam = firstEvent.getPropertyParameters(VEvent.DTEND); - if (dtEnd != null && !TextUtils.isEmpty(dtEnd)) { - calIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, - getLocalTimeFromString(dtEnd, dtEndParam)); - } else { - // Treat start date as end date if un-specified - dtEnd = dtStart; - dtEndParam = dtStartParam; - } + String dtEnd = firstEvent.getProperty(VEvent.DTEND); + String dtEndParam = firstEvent.getPropertyParameters(VEvent.DTEND); + if (dtEnd != null && !TextUtils.isEmpty(dtEnd)) { + calIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, + getLocalTimeFromString(dtEnd, dtEndParam)); + } else { + // Treat start date as end date if un-specified + dtEnd = dtStart; + dtEndParam = dtStartParam; + } - boolean isAllDay = getLocalTimeFromString(dtEnd, dtEndParam) - - getLocalTimeFromString(dtStart, dtStartParam) == 86400000; + boolean isAllDay = getLocalTimeFromString(dtEnd, dtEndParam) + - getLocalTimeFromString(dtStart, dtStartParam) == 86400000; - if (isTimeStartOfDay(dtStart, dtStartParam)) { - calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay); - } - //Check if some special property which say it is a "All-Day" event. + if (isTimeStartOfDay(dtStart, dtStartParam)) { + calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay); + } + //Check if some special property which say it is a "All-Day" event. - String microsoft_all_day_event = firstEvent.getProperty("X-MICROSOFT-CDO-ALLDAYEVENT"); - if(!TextUtils.isEmpty(microsoft_all_day_event) && microsoft_all_day_event.equals("TRUE")){ - calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, true); - } + String microsoft_all_day_event = firstEvent.getProperty("X-MICROSOFT-CDO-ALLDAYEVENT"); + if (!TextUtils.isEmpty(microsoft_all_day_event) && microsoft_all_day_event.equals("TRUE")) { + calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, true); + } - calIntent.putExtra(EditEventActivity.EXTRA_READ_ONLY, true); + calIntent.putExtra(EditEventActivity.EXTRA_READ_ONLY, true); - try { - startActivity(calIntent); - } catch (ActivityNotFoundException e) { - // Oh well... - } finally { - finish(); + try { + startActivity(calIntent); + } catch (ActivityNotFoundException e) { + // Oh well... + } finally { + finish(); + } } } -- GitLab From bc7f84fdc9a1df4ae08fb393d7620e7f63a7f2db Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 8 Aug 2024 17:50:29 +0200 Subject: [PATCH 03/45] refactor(ImportActivity): extract code to start 'EditEventActivity' from 'parseCalFile() into a dedicated method' method --- .../com/android/calendar/ImportActivity.java | 119 +++++++++--------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/com/android/calendar/ImportActivity.java b/app/src/main/java/com/android/calendar/ImportActivity.java index 48761dc36..92530df90 100644 --- a/app/src/main/java/com/android/calendar/ImportActivity.java +++ b/app/src/main/java/com/android/calendar/ImportActivity.java @@ -149,74 +149,77 @@ public class ImportActivity extends Activity { } if (events.size() == 1) { - 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))); - calIntent.putExtra(CalendarContract.Events.EVENT_LOCATION, - IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.LOCATION))); - calIntent.putExtra(CalendarContract.Events.DESCRIPTION, - IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.DESCRIPTION))); - calIntent.putExtra(ExtendedProperty.URL, - IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.URL))); - calIntent.putExtra(CalendarContract.Events.ORGANIZER, - IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.ORGANIZER))); - calIntent.putExtra(CalendarContract.Events.RRULE, - IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.RRULE))); - - if (firstEvent.mAttendees.size() > 0) { - StringBuilder builder = new StringBuilder(); - for (Attendee attendee : firstEvent.mAttendees) { - builder.append(attendee.mEmail); - builder.append(","); - } - calIntent.putExtra(Intent.EXTRA_EMAIL, builder.toString()); - } + VEvent event = calendar.getAllEvents().getFirst(); + startInsertEventActivity(event); + } + } - String dtStart = firstEvent.getProperty(VEvent.DTSTART); - String dtStartParam = firstEvent.getPropertyParameters(VEvent.DTSTART); - if (!TextUtils.isEmpty(dtStart)) { - calIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, - getLocalTimeFromString(dtStart, dtStartParam)); + private void startInsertEventActivity(VEvent event) { + Intent calIntent = new Intent(Intent.ACTION_INSERT); + calIntent.setType("vnd.android.cursor.item/event"); + + calIntent.putExtra(CalendarContract.Events.TITLE, + IcalendarUtils.uncleanseString(event.getProperty(VEvent.SUMMARY))); + calIntent.putExtra(CalendarContract.Events.EVENT_LOCATION, + IcalendarUtils.uncleanseString(event.getProperty(VEvent.LOCATION))); + calIntent.putExtra(CalendarContract.Events.DESCRIPTION, + IcalendarUtils.uncleanseString(event.getProperty(VEvent.DESCRIPTION))); + calIntent.putExtra(ExtendedProperty.URL, + IcalendarUtils.uncleanseString(event.getProperty(VEvent.URL))); + calIntent.putExtra(CalendarContract.Events.ORGANIZER, + IcalendarUtils.uncleanseString(event.getProperty(VEvent.ORGANIZER))); + calIntent.putExtra(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(","); } + calIntent.putExtra(Intent.EXTRA_EMAIL, builder.toString()); + } - String dtEnd = firstEvent.getProperty(VEvent.DTEND); - String dtEndParam = firstEvent.getPropertyParameters(VEvent.DTEND); - if (dtEnd != null && !TextUtils.isEmpty(dtEnd)) { - calIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, - getLocalTimeFromString(dtEnd, dtEndParam)); - } else { - // Treat start date as end date if un-specified - dtEnd = dtStart; - dtEndParam = dtStartParam; - } + String dtStart = event.getProperty(VEvent.DTSTART); + String dtStartParam = event.getPropertyParameters(VEvent.DTSTART); + if (!TextUtils.isEmpty(dtStart)) { + calIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, + getLocalTimeFromString(dtStart, dtStartParam)); + } - boolean isAllDay = getLocalTimeFromString(dtEnd, dtEndParam) - - getLocalTimeFromString(dtStart, dtStartParam) == 86400000; + String dtEnd = event.getProperty(VEvent.DTEND); + String dtEndParam = event.getPropertyParameters(VEvent.DTEND); + if (dtEnd != null && !TextUtils.isEmpty(dtEnd)) { + calIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, + getLocalTimeFromString(dtEnd, dtEndParam)); + } else { + // Treat start date as end date if un-specified + dtEnd = dtStart; + dtEndParam = dtStartParam; + } + boolean isAllDay = getLocalTimeFromString(dtEnd, dtEndParam) + - getLocalTimeFromString(dtStart, dtStartParam) == 86400000; - if (isTimeStartOfDay(dtStart, dtStartParam)) { - calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay); - } - //Check if some special property which say it is a "All-Day" event. - String microsoft_all_day_event = firstEvent.getProperty("X-MICROSOFT-CDO-ALLDAYEVENT"); - if (!TextUtils.isEmpty(microsoft_all_day_event) && microsoft_all_day_event.equals("TRUE")) { - calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, true); - } + if (isTimeStartOfDay(dtStart, dtStartParam)) { + calIntent.putExtra(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")) { + calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, true); + } - calIntent.putExtra(EditEventActivity.EXTRA_READ_ONLY, true); + calIntent.putExtra(EditEventActivity.EXTRA_READ_ONLY, true); - try { - startActivity(calIntent); - } catch (ActivityNotFoundException e) { - // Oh well... - } finally { - finish(); - } + try { + startActivity(calIntent); + } catch (ActivityNotFoundException e) { + // Oh well... + } finally { + finish(); } } -- GitLab From 83065fe0969861f83a3315e76d067ea4a4b7b2fa Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 8 Aug 2024 17:55:25 +0200 Subject: [PATCH 04/45] task: prepare else statement for inserting multiple event --- app/src/main/java/com/android/calendar/ImportActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/android/calendar/ImportActivity.java b/app/src/main/java/com/android/calendar/ImportActivity.java index 92530df90..2f09bbb8f 100644 --- a/app/src/main/java/com/android/calendar/ImportActivity.java +++ b/app/src/main/java/com/android/calendar/ImportActivity.java @@ -151,6 +151,8 @@ public class ImportActivity extends Activity { if (events.size() == 1) { VEvent event = calendar.getAllEvents().getFirst(); startInsertEventActivity(event); + } else { + //todo handle multi event insertion } } -- GitLab From 96fe5a87de69cb079e49d2997352b92fc207bcdd Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 12 Aug 2024 17:52:12 +0200 Subject: [PATCH 05/45] task: add String values --- .../event/PickCalendarDialogFragment.java | 58 +++++++++++++++++++ app/src/main/res/values/strings.xml | 12 +++- 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java diff --git a/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java b/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java new file mode 100644 index 000000000..db8f7a297 --- /dev/null +++ b/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java @@ -0,0 +1,58 @@ +package com.android.calendar.event; + + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import androidx.fragment.app.DialogFragment; + +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Spinner; + +import ws.xsoh.etar.R; + +/** + * Allows the user to quickly import a multi event cal file. + */ +public class ImportMultiEventDialogFragment extends DialogFragment { + + private static final String TAG = "ImportMultipleEventDialogFragment"; + + private AlertDialog mAlertDialog; + Spinner mCalendarsSpinner; + + public ImportMultiEventDialogFragment() { + // Empty constructor required for DialogFragment. + } + + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + final LayoutInflater layoutInflater = (LayoutInflater) activity + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + final View view = layoutInflater.inflate(R.layout.import_multi_event_dialog, null); + mCalendarsSpinner = (Spinner) view.findViewById(R.id.calendars_spinner); + + + mAlertDialog = new AlertDialog.Builder(activity) + .setTitle(R.string.import_multi_event_dialog_title) + .setView(view) + .setPositiveButton(R.string.import_multi_event_dialog_validate, + (dialog, which) -> { + //todo may be it could be better to have the listener define in importActivity + dismiss(); + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + + return mAlertDialog; + } + + +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3799adcd5..2dec60ebc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -197,7 +197,17 @@ "Responded no." - + + + + + Ok + + Import events + + In which calendar do you want to import these x events? + + The events have been created successfully. -- GitLab From 9f103015ebbca56612737885590c83b4b7f02a56 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 12 Aug 2024 18:08:10 +0200 Subject: [PATCH 06/45] task(UX): create PickCalendarDialogFragment & pickCalendarDialog layout --- .../event/PickCalendarDialogFragment.java | 13 +-- .../main/res/layout/pick_calendar_dialog.xml | 105 ++++++++++++++++++ 2 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 app/src/main/res/layout/pick_calendar_dialog.xml diff --git a/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java b/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java index db8f7a297..8e967071c 100644 --- a/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java @@ -7,7 +7,6 @@ import android.app.Dialog; import androidx.fragment.app.DialogFragment; import android.content.Context; -import android.content.DialogInterface; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -18,14 +17,14 @@ import ws.xsoh.etar.R; /** * Allows the user to quickly import a multi event cal file. */ -public class ImportMultiEventDialogFragment extends DialogFragment { +public class PickCalendarDialogFragment extends DialogFragment { - private static final String TAG = "ImportMultipleEventDialogFragment"; + private static final String TAG = "PickCalendarDialogFragment"; private AlertDialog mAlertDialog; Spinner mCalendarsSpinner; - public ImportMultiEventDialogFragment() { + public PickCalendarDialogFragment() { // Empty constructor required for DialogFragment. } @@ -36,14 +35,14 @@ public class ImportMultiEventDialogFragment extends DialogFragment { final LayoutInflater layoutInflater = (LayoutInflater) activity .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - final View view = layoutInflater.inflate(R.layout.import_multi_event_dialog, null); + final View view = layoutInflater.inflate(R.layout.pick_calendar_dialog, null); mCalendarsSpinner = (Spinner) view.findViewById(R.id.calendars_spinner); mAlertDialog = new AlertDialog.Builder(activity) - .setTitle(R.string.import_multi_event_dialog_title) + .setTitle(R.string.pick_calendar_dialog_title) .setView(view) - .setPositiveButton(R.string.import_multi_event_dialog_validate, + .setPositiveButton(R.string.pick_calendar_dialog_validate, (dialog, which) -> { //todo may be it could be better to have the listener define in importActivity dismiss(); diff --git a/app/src/main/res/layout/pick_calendar_dialog.xml b/app/src/main/res/layout/pick_calendar_dialog.xml new file mode 100644 index 000000000..68fdc59e1 --- /dev/null +++ b/app/src/main/res/layout/pick_calendar_dialog.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + -- GitLab From 7a9d3f3f91f18080058920701780730fe44a3a71 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 13 Aug 2024 15:46:42 +0200 Subject: [PATCH 07/45] task: make dialog to be displayed (for test) --- .../main/java/com/android/calendar/ImportActivity.java | 8 ++++++++ .../calendar/event/PickCalendarDialogFragment.java | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/android/calendar/ImportActivity.java b/app/src/main/java/com/android/calendar/ImportActivity.java index 2f09bbb8f..42dc85b9a 100644 --- a/app/src/main/java/com/android/calendar/ImportActivity.java +++ b/app/src/main/java/com/android/calendar/ImportActivity.java @@ -2,6 +2,7 @@ package com.android.calendar; import android.app.Activity; import android.app.AlertDialog; +import android.app.FragmentManager; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.DialogInterface; @@ -16,6 +17,7 @@ import android.widget.Toast; import com.android.calendar.event.EditEventActivity; import com.android.calendar.event.ExtendedProperty; +import com.android.calendar.event.PickCalendarDialogFragment; import com.android.calendar.icalendar.Attendee; import com.android.calendar.icalendar.IcalendarUtils; import com.android.calendar.icalendar.VCalendar; @@ -34,6 +36,7 @@ import ws.xsoh.etar.R; public class ImportActivity extends Activity { + private PickCalendarDialogFragment mPickCalendarDialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -152,6 +155,11 @@ public class ImportActivity extends Activity { VEvent event = calendar.getAllEvents().getFirst(); startInsertEventActivity(event); } else { + final FragmentManager fragmentManager = getFragmentManager(); + if (fragmentManager != null) { + mPickCalendarDialog = new PickCalendarDialogFragment(); + mPickCalendarDialog.show(fragmentManager, PickCalendarDialogFragment.FRAGMENT_TAG); + } //todo handle multi event insertion } } diff --git a/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java b/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java index 8e967071c..0efe7564d 100644 --- a/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java @@ -4,8 +4,8 @@ package com.android.calendar.event; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; -import androidx.fragment.app.DialogFragment; +import android.app.DialogFragment; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; @@ -20,6 +20,7 @@ import ws.xsoh.etar.R; public class PickCalendarDialogFragment extends DialogFragment { private static final String TAG = "PickCalendarDialogFragment"; + public static final String FRAGMENT_TAG = "PICK_CALENDAR_DIALOG"; private AlertDialog mAlertDialog; Spinner mCalendarsSpinner; -- GitLab From 69c694026fcb2dc5bfea68ee392d1bd2845afd04 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 14 Aug 2024 14:13:07 +0200 Subject: [PATCH 08/45] task: temporarily disabled textView's style property which caused layout inflation failure --- app/src/main/res/layout/pick_calendar_dialog.xml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/layout/pick_calendar_dialog.xml b/app/src/main/res/layout/pick_calendar_dialog.xml index 68fdc59e1..7b431088d 100644 --- a/app/src/main/res/layout/pick_calendar_dialog.xml +++ b/app/src/main/res/layout/pick_calendar_dialog.xml @@ -2,7 +2,6 @@ @@ -54,11 +53,10 @@ android:paddingEnd="2dp" app:layout_constraintBottom_toBottomOf="@+id/calendar_selector_group_icon" app:layout_constraintStart_toEndOf="@+id/calendar_selector_group_icon" - app:layout_constraintTop_toTopOf="@+id/calendar_selector_group_icon" /> + app:layout_constraintTop_toTopOf="@+id/calendar_selector_group_icon"/> + + + tools:layout_conversion_absoluteWidth="363dp"/> + - -- GitLab From 16fa6900a9020b2d55e771593cb5d7b6dd3f3b83 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 16 Aug 2024 11:53:37 +0200 Subject: [PATCH 09/45] task: pass multiple event through intent to EditEventActivity --- .../com/android/calendar/ImportActivity.java | 98 ++++++++--- .../calendar/event/EditEventActivity.java | 166 +++++++++++------- .../calendar/icalendar/IcalendarUtils.java | 19 ++ app/src/main/res/layout/import_activity.xml | 6 + 4 files changed, 200 insertions(+), 89 deletions(-) create mode 100644 app/src/main/res/layout/import_activity.xml diff --git a/app/src/main/java/com/android/calendar/ImportActivity.java b/app/src/main/java/com/android/calendar/ImportActivity.java index 42dc85b9a..f8549fa7e 100644 --- a/app/src/main/java/com/android/calendar/ImportActivity.java +++ b/app/src/main/java/com/android/calendar/ImportActivity.java @@ -13,6 +13,7 @@ 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 com.android.calendar.event.EditEventActivity; @@ -27,19 +28,24 @@ 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.Map; import java.util.TimeZone; import ws.xsoh.etar.R; public class ImportActivity extends Activity { - private PickCalendarDialogFragment mPickCalendarDialog; + //private PickCalendarDialogFragment mPickCalendarDialog; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + //setContentView(R.layout.import_activity); if (!isValidIntent()) { Toast.makeText(this, R.string.cal_nothing_to_import, Toast.LENGTH_SHORT).show(); finish(); @@ -138,6 +144,23 @@ public class ImportActivity extends Activity { private void parseCalFile() { Uri uri = getIntent().getData(); + /*int eventCount = IcalendarUtils.getEventCountFromCalendarFile(this, uri); + if (eventCount == 1) { + Intent calIntent = new Intent(Intent.ACTION_INSERT); + calIntent.setType("vnd.android.cursor.item/event"); + calIntent.putExtra("FILE_URI", uri); + startActivity(insertMultipleEventIntent); + } else if (eventCount > 1) { + Intent insertMultipleEventIntent = Intent(Intent.ACTION_INSERT_MULTIPLE); // to insert multiple file + insertMultipleEventIntent.putExtra("File_URI", uri) + startActivity(insertMultipleEventIntent); + } else { + showErrorToast(); + + } + finish(); + */ + VCalendar calendar = IcalendarUtils.readCalendarFromFile(this, uri); if (calendar == null) { @@ -145,40 +168,61 @@ public class ImportActivity extends Activity { return; } - LinkedList events = calendar.getAllEvents(); + final LinkedList events = calendar.getAllEvents(); + if (events == null || events.isEmpty()) { showErrorToast(); return; } if (events.size() == 1) { + Log.d("Vincent", "Try to insert a single event"); VEvent event = calendar.getAllEvents().getFirst(); startInsertEventActivity(event); } else { - final FragmentManager fragmentManager = getFragmentManager(); - if (fragmentManager != null) { - mPickCalendarDialog = new PickCalendarDialogFragment(); - mPickCalendarDialog.show(fragmentManager, PickCalendarDialogFragment.FRAGMENT_TAG); + final Intent calIntent = new Intent(Intent.ACTION_INSERT); + calIntent.setType("vnd.android.cursor.item/event"); + + final ArrayList bundleList = new ArrayList<>(); + for (VEvent event : events) { + bundleList.add(putEventInBundle(event)); } - //todo handle multi event insertion + + calIntent.putParcelableArrayListExtra("FAHIM", bundleList); + startActivity(calIntent); + finish(); + //displayPickCalendarDialog(); } } - private void startInsertEventActivity(VEvent event) { - Intent calIntent = new Intent(Intent.ACTION_INSERT); - calIntent.setType("vnd.android.cursor.item/event"); + private void displayPickCalendarDialog() { + final FragmentManager fragmentManager = getFragmentManager(); + if (fragmentManager != null) { + //setContentView(R.layout.import_activity); + Log.d("Vincent", "instanciate pickCalendarDialogFragment"); + //mPickCalendarDialog = new PickCalendarDialogFragment(); + Log.d("Vincent", "display Pick calendar dialog"); + //mPickCalendarDialog.show(fragmentManager, PickCalendarDialogFragment.FRAGMENT_TAG); - calIntent.putExtra(CalendarContract.Events.TITLE, + } else { + Log.d("Vincent", "fragmentManager is null"); + } + } + + + private Bundle putEventInBundle(VEvent event) { + Bundle bundle = new Bundle(); + bundle.putString(CalendarContract.Events.TITLE, IcalendarUtils.uncleanseString(event.getProperty(VEvent.SUMMARY))); - calIntent.putExtra(CalendarContract.Events.EVENT_LOCATION, + bundle.putString(CalendarContract.Events.EVENT_LOCATION, IcalendarUtils.uncleanseString(event.getProperty(VEvent.LOCATION))); - calIntent.putExtra(CalendarContract.Events.DESCRIPTION, + bundle.putString(CalendarContract.Events.DESCRIPTION, IcalendarUtils.uncleanseString(event.getProperty(VEvent.DESCRIPTION))); - calIntent.putExtra(ExtendedProperty.URL, + bundle.putString(ExtendedProperty.URL, IcalendarUtils.uncleanseString(event.getProperty(VEvent.URL))); - calIntent.putExtra(CalendarContract.Events.ORGANIZER, + bundle.putString(CalendarContract.Events.ORGANIZER, IcalendarUtils.uncleanseString(event.getProperty(VEvent.ORGANIZER))); - calIntent.putExtra(CalendarContract.Events.RRULE, + bundle.putString(CalendarContract.Events.RRULE, IcalendarUtils.uncleanseString(event.getProperty(VEvent.RRULE))); if (event.mAttendees.size() > 0) { @@ -187,20 +231,20 @@ public class ImportActivity extends Activity { builder.append(attendee.mEmail); builder.append(","); } - calIntent.putExtra(Intent.EXTRA_EMAIL, builder.toString()); + bundle.putString(Intent.EXTRA_EMAIL, builder.toString()); } String dtStart = event.getProperty(VEvent.DTSTART); String dtStartParam = event.getPropertyParameters(VEvent.DTSTART); if (!TextUtils.isEmpty(dtStart)) { - calIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, + bundle.putLong(CalendarContract.EXTRA_EVENT_BEGIN_TIME, getLocalTimeFromString(dtStart, dtStartParam)); } String dtEnd = event.getProperty(VEvent.DTEND); String dtEndParam = event.getPropertyParameters(VEvent.DTEND); if (dtEnd != null && !TextUtils.isEmpty(dtEnd)) { - calIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, + bundle.putLong(CalendarContract.EXTRA_EVENT_END_TIME, getLocalTimeFromString(dtEnd, dtEndParam)); } else { // Treat start date as end date if un-specified @@ -213,16 +257,26 @@ public class ImportActivity extends Activity { if (isTimeStartOfDay(dtStart, dtStartParam)) { - calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay); + 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")) { - calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, true); + bundle.putBoolean(CalendarContract.EXTRA_EVENT_ALL_DAY, true); } - calIntent.putExtra(EditEventActivity.EXTRA_READ_ONLY, true); + bundle.putBoolean(EditEventActivity.EXTRA_READ_ONLY, true); + + return bundle; + } + + private void startInsertEventActivity(VEvent event) { + Intent calIntent = new Intent(Intent.ACTION_INSERT); + calIntent.setType("vnd.android.cursor.item/event"); + + Bundle bundle = putEventInBundle(event); + calIntent.putExtra("bundle", bundle); try { startActivity(calIntent); diff --git a/app/src/main/java/com/android/calendar/event/EditEventActivity.java b/app/src/main/java/com/android/calendar/event/EditEventActivity.java index 2a5dbb107..438559d94 100644 --- a/app/src/main/java/com/android/calendar/event/EditEventActivity.java +++ b/app/src/main/java/com/android/calendar/event/EditEventActivity.java @@ -23,9 +23,11 @@ import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.os.Parcelable; import android.provider.CalendarContract.Events; import android.util.Log; import android.view.MenuItem; +import android.widget.Toast; import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.Toolbar; @@ -37,8 +39,10 @@ import com.android.calendar.CalendarController.EventInfo; import com.android.calendar.CalendarEventModel.ReminderEntry; import com.android.calendar.DynamicTheme; import com.android.calendar.Utils; +import com.android.calendar.icalendar.IcalendarUtils; +import com.android.calendar.icalendar.VEvent; import com.android.calendarcommon2.Time; - +import com.android.calendar.icalendar.VCalendar; import java.util.ArrayList; import ws.xsoh.etar.R; @@ -61,6 +65,8 @@ public class EditEventActivity extends AbstractCalendarActivity { private boolean mEventColorInitialized; + private ArrayList mEventInfos; + private EventInfo mEventInfo; @Override @@ -69,49 +75,57 @@ public class EditEventActivity extends AbstractCalendarActivity { dynamicTheme.onCreate(this); setContentView(R.layout.simple_frame_layout_material); - mEventInfo = getEventInfoFromIntent(icicle); - mReminders = getReminderEntriesFromIntent(); - mEventColorInitialized = getIntent().hasExtra(EXTRA_EVENT_COLOR); - mEventColor = getIntent().getIntExtra(EXTRA_EVENT_COLOR, -1); - Toolbar myToolbar = findViewById(R.id.toolbar); - setSupportActionBar(myToolbar); - - mEditFragment = (EditEventFragment) getSupportFragmentManager().findFragmentById(R.id.body_frame); - - mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config); - - if (mIsMultipane) { - getSupportActionBar().setDisplayOptions( - ActionBar.DISPLAY_SHOW_TITLE, - ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME - | ActionBar.DISPLAY_SHOW_TITLE); - getSupportActionBar().setTitle( - mEventInfo.id == -1 ? R.string.event_create : R.string.event_edit); - } - else { - getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, - ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME| - ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM); - } - if (mEditFragment == null) { - Intent intent = null; - boolean readOnly = false; - if (mEventInfo.id == -1) { - intent = getIntent(); - readOnly = intent.getBooleanExtra(EXTRA_READ_ONLY, false); + mEventInfos = getEventInfoFromIntent(icicle); + + + if (mEventInfos.size() == 1) { + mEventInfo = mEventInfos.get(0); //get first + mReminders = getReminderEntriesFromIntent(); + mEventColorInitialized = getIntent().hasExtra(EXTRA_EVENT_COLOR); + mEventColor = getIntent().getIntExtra(EXTRA_EVENT_COLOR, -1); + Toolbar myToolbar = findViewById(R.id.toolbar); + setSupportActionBar(myToolbar); + + mEditFragment = (EditEventFragment) getSupportFragmentManager().findFragmentById(R.id.body_frame); + + mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config); + + if (mIsMultipane) { + getSupportActionBar().setDisplayOptions( + ActionBar.DISPLAY_SHOW_TITLE, + ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME + | ActionBar.DISPLAY_SHOW_TITLE); + getSupportActionBar().setTitle( + mEventInfo.id == -1 ? R.string.event_create : R.string.event_edit); } + else { + getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, + ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME| + ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM); + } + + if (mEditFragment == null) { + Intent intent = null; + boolean readOnly = false; + if (mEventInfo.id == -1) { + intent = getIntent(); + readOnly = intent.getBooleanExtra(EXTRA_READ_ONLY, false); + } - mEditFragment = new EditEventFragment(mEventInfo, mReminders, mEventColorInitialized, - mEventColor, readOnly, intent); + mEditFragment = new EditEventFragment(mEventInfo, mReminders, mEventColorInitialized, + mEventColor, readOnly, intent); - mEditFragment.mShowModifyDialogOnLaunch = getIntent().getBooleanExtra( - CalendarController.EVENT_EDIT_ON_LAUNCH, false); + mEditFragment.mShowModifyDialogOnLaunch = getIntent().getBooleanExtra( + CalendarController.EVENT_EDIT_ON_LAUNCH, false); - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - ft.replace(R.id.body_frame, mEditFragment); - ft.show(mEditFragment); - ft.commit(); + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.replace(R.id.body_frame, mEditFragment); + ft.show(mEditFragment); + ft.commit(); + } + } else if(mEventInfos.size() > 1) { + Toast.makeText(this, "Try to import " + mEventInfos.size() + "Bundle", Toast.LENGTH_SHORT).show(); } } @@ -121,51 +135,69 @@ public class EditEventActivity extends AbstractCalendarActivity { return (ArrayList) intent.getSerializableExtra(EXTRA_EVENT_REMINDERS); } - private EventInfo getEventInfoFromIntent(Bundle icicle) { - EventInfo info = new EventInfo(); - long eventId = -1; - Intent intent = getIntent(); - Uri data = intent.getData(); - if (data != null) { - try { - eventId = Long.parseLong(data.getLastPathSegment()); - } catch (NumberFormatException e) { - if (DEBUG) { - Log.d(TAG, "Create new event"); + private ArrayList getEventInfoFromIntent(Bundle icicle) { + ArrayList eventInfos = new ArrayList<>(); + + final Intent intent = getIntent(); + + ArrayList bundles = intent.getParcelableArrayListExtra("FAHIM"); + + if (bundles == null) return eventInfos; + + 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); } - } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) { - eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID); + + final EventInfo eventInfo = loadEventInfoFromBundle(eventId, bundle); + eventInfos.add(eventInfo); } - boolean allDay = intent.getBooleanExtra(EXTRA_EVENT_ALL_DAY, false); + return eventInfos; + } + + private EventInfo loadEventInfoFromBundle(long eventId, Bundle bundle) { + EventInfo eventInfo = new EventInfo(); - long begin = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1); - long end = intent.getLongExtra(EXTRA_EVENT_END_TIME, -1); + boolean allDay = bundle.getBoolean(EXTRA_EVENT_ALL_DAY, false); + + long begin = bundle.getLong(EXTRA_EVENT_BEGIN_TIME, -1); + long end = bundle.getLong(EXTRA_EVENT_END_TIME, -1); if (end != -1) { - info.endTime = new Time(); + eventInfo.endTime = new Time(); if (allDay) { - info.endTime.setTimezone(Time.TIMEZONE_UTC); + eventInfo.endTime.setTimezone(Time.TIMEZONE_UTC); } - info.endTime.set(end); + eventInfo.endTime.set(end); } if (begin != -1) { - info.startTime = new Time(); + eventInfo.startTime = new Time(); if (allDay) { - info.startTime.setTimezone(Time.TIMEZONE_UTC); + eventInfo.startTime.setTimezone(Time.TIMEZONE_UTC); } - info.startTime.set(begin); + eventInfo.startTime.set(begin); } - info.id = eventId; - info.eventTitle = intent.getStringExtra(Events.TITLE); - info.calendarId = intent.getLongExtra(Events.CALENDAR_ID, -1); + eventInfo.id = eventId; + eventInfo.eventTitle = bundle.getString(Events.TITLE); + eventInfo.calendarId = bundle.getLong(Events.CALENDAR_ID, -1); if (allDay) { - info.extraLong = CalendarController.EXTRA_CREATE_ALL_DAY; + eventInfo.extraLong = CalendarController.EXTRA_CREATE_ALL_DAY; } else { - info.extraLong = 0; + eventInfo.extraLong = 0; } - return info; + + return eventInfo; } @Override diff --git a/app/src/main/java/com/android/calendar/icalendar/IcalendarUtils.java b/app/src/main/java/com/android/calendar/icalendar/IcalendarUtils.java index 8473cb385..0657d7c1f 100644 --- a/app/src/main/java/com/android/calendar/icalendar/IcalendarUtils.java +++ b/app/src/main/java/com/android/calendar/icalendar/IcalendarUtils.java @@ -140,6 +140,25 @@ public class IcalendarUtils { return calendar; } + public static int getEventCountFromCalendarFile(Context context, Uri uri) { + /* + TODO alternative method to avoid parsing everything: + loop through each line in the file + count "BEGIN:VEVENT" number + count "END:VEVENT" number + if event doesn't match : invalid file + return VEVENT counter + + CHECK the VCalendar.populateFromString for idea + */ + VCalendar calendar = readCalendarFromFile(context, uri); + if (calendar != null) + return calendar.getAllEvents().size(); + else return 0; + } + + + public static ArrayList getStringArrayFromFile(Context context, Uri uri) { String scheme = uri.getScheme(); InputStream inputStream = null; diff --git a/app/src/main/res/layout/import_activity.xml b/app/src/main/res/layout/import_activity.xml new file mode 100644 index 000000000..135440848 --- /dev/null +++ b/app/src/main/res/layout/import_activity.xml @@ -0,0 +1,6 @@ + + + + -- GitLab From bdce14ffc866f7ff8f899ef01494cf73bc6c4ebd Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Mon, 19 Aug 2024 13:05:31 +0600 Subject: [PATCH 10/45] feat: import multiple events from .ics file Events are parsed in ImportActivity and sent to AllInOneActivity. User gets a prompt to choose the calendar to save the events. TODO: Integration with the CalendarPickerDialogFragment. --- app/src/main/AndroidManifest.xml | 1 - .../android/calendar/AllInOneActivity.java | 77 +++++- .../java/com/android/calendar/EventUtils.java | 234 ++++++++++++++++++ .../com/android/calendar/ImportActivity.java | 137 ++++------ ...java => CalendarPickerDialogFragment.java} | 10 +- .../calendar/event/EditEventActivity.java | 166 +++++-------- 6 files changed, 429 insertions(+), 196 deletions(-) create mode 100644 app/src/main/java/com/android/calendar/EventUtils.java rename app/src/main/java/com/android/calendar/event/{PickCalendarDialogFragment.java => CalendarPickerDialogFragment.java} (89%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fbef430a0..1e25cea32 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,7 +19,6 @@ --> diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 23804e0e9..6d56d35dc 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -23,10 +23,10 @@ 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.DatePickerDialog; import android.content.AsyncQueryHandler; import android.content.BroadcastReceiver; @@ -83,6 +83,7 @@ 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.month.MonthByWeekFragment; import com.android.calendar.selectcalendars.SelectVisibleCalendarsFragment; import com.android.calendar.settings.GeneralPreferences; @@ -105,6 +106,7 @@ import ws.xsoh.etar.R; public class AllInOneActivity extends AbstractCalendarActivity implements EventHandler, OnSharedPreferenceChangeListener, SearchView.OnQueryTextListener, SearchView.OnSuggestionListener, NavigationView.OnNavigationItemSelectedListener { + 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"; @@ -235,6 +237,25 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH private AllInOneMenuExtensionsInterface mExtensions = ExtensionsFactory .getAllInOneMenuExtensions(); + private void showCalendarChooserDialog(List eventList) { + final FragmentManager fragmentManager = getSupportFragmentManager(); + CalendarPickerDialogFragment fragment = + ((CalendarPickerDialogFragment) fragmentManager.findFragmentByTag(CalendarPickerDialogFragment.FRAGMENT_TAG)); + + if (fragment != null) { + fragment.dismiss(); + } + + fragment = new CalendarPickerDialogFragment(); + fragment.show(fragmentManager, CalendarPickerDialogFragment.FRAGMENT_TAG); + + // FIXME: 19/08/2024 Remove when implementation done + String message = String.format(Locale.getDefault(), "Found %d events from .ics file import", eventList.size()); + Log.i(TAG, message); + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); + } + + @Override protected void onNewIntent(Intent intent) { String action = intent.getAction(); @@ -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,51 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH prefs.registerOnSharedPreferenceChangeListener(this); mContentResolver = getContentResolver(); + + // Ignore handling events on activity recreation on configuration changes + if (icicle == null) { + handleEvents(icicle, getIntent()); + } + } + + private void handleEvents(Bundle bundle, Intent intent) { + final List eventList = getEventList(bundle, intent); + if (eventList.isEmpty()) { + return; + } + // TODO: 19/08/2024 Decide to show dialog for consecutive .ics import. Now it replaces previous one. + showCalendarChooserDialog(eventList); + } + + private List getEventList(Bundle icicle, Intent intent) { + List eventInfoList = new ArrayList<>(); + + final ArrayList bundles = intent.getParcelableArrayListExtra(AllInOneActivity.BUNDLE_KEY_MULTIPLE_EVENTS); + + if (bundles == null || bundles.isEmpty()) { + return eventInfoList; + } + + 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); + } + + final EventInfo eventInfo = EventUtils.crateEventInfoFromBundle(eventId, bundle); + eventInfoList.add(eventInfo); + } + + return eventInfoList; } private void checkAppPermissions() { diff --git a/app/src/main/java/com/android/calendar/EventUtils.java b/app/src/main/java/com/android/calendar/EventUtils.java new file mode 100644 index 000000000..9d895aa2e --- /dev/null +++ b/app/src/main/java/com/android/calendar/EventUtils.java @@ -0,0 +1,234 @@ +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.os.Build; +import android.os.Bundle; +import android.provider.CalendarContract; +import android.text.TextUtils; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.calendar.CalendarController.EventInfo; +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 com.android.calendarcommon2.Time; + +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 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) == 86400000; + + + 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; + } + + @NonNull + public static EventInfo crateEventInfoFromBundle(long eventId, @Nullable Bundle bundle) { + EventInfo eventInfo = new EventInfo(); + + if (bundle == null) return eventInfo; + + boolean allDay = bundle.getBoolean(EXTRA_EVENT_ALL_DAY, false); + + long begin = bundle.getLong(EXTRA_EVENT_BEGIN_TIME, -1); + long end = bundle.getLong(EXTRA_EVENT_END_TIME, -1); + if (end != -1) { + eventInfo.endTime = new Time(); + if (allDay) { + eventInfo.endTime.setTimezone(Time.TIMEZONE_UTC); + } + eventInfo.endTime.set(end); + } + if (begin != -1) { + eventInfo.startTime = new Time(); + if (allDay) { + eventInfo.startTime.setTimezone(Time.TIMEZONE_UTC); + } + eventInfo.startTime.set(begin); + } + eventInfo.id = eventId; + eventInfo.eventTitle = bundle.getString(CalendarContract.Events.TITLE); + eventInfo.calendarId = bundle.getLong(CalendarContract.Events.CALENDAR_ID, -1); + + if (allDay) { + eventInfo.extraLong = CalendarController.EXTRA_CREATE_ALL_DAY; + } else { + eventInfo.extraLong = 0; + } + + return eventInfo; + } + + 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 e) { + } + } + + // 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 e) { + } + } + + // 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 e) { + } + } + + // 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 e) { + } + } + + Toast.makeText(context, context.getString(R.string.cal_import_error_date_msg, iCalDate), Toast.LENGTH_SHORT).show(); + + return System.currentTimeMillis(); + } +} diff --git a/app/src/main/java/com/android/calendar/ImportActivity.java b/app/src/main/java/com/android/calendar/ImportActivity.java index f8549fa7e..33122d7dc 100644 --- a/app/src/main/java/com/android/calendar/ImportActivity.java +++ b/app/src/main/java/com/android/calendar/ImportActivity.java @@ -2,7 +2,6 @@ package com.android.calendar; import android.app.Activity; import android.app.AlertDialog; -import android.app.FragmentManager; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.DialogInterface; @@ -13,12 +12,10 @@ 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 com.android.calendar.event.EditEventActivity; import com.android.calendar.event.ExtendedProperty; -import com.android.calendar.event.PickCalendarDialogFragment; import com.android.calendar.icalendar.Attendee; import com.android.calendar.icalendar.IcalendarUtils; import com.android.calendar.icalendar.VCalendar; @@ -33,19 +30,15 @@ import java.util.Arrays; import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.TimeZone; import ws.xsoh.etar.R; public class ImportActivity extends Activity { - //private PickCalendarDialogFragment mPickCalendarDialog; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - //setContentView(R.layout.import_activity); if (!isValidIntent()) { Toast.makeText(this, R.string.cal_nothing_to_import, Toast.LENGTH_SHORT).show(); finish(); @@ -144,23 +137,6 @@ public class ImportActivity extends Activity { private void parseCalFile() { Uri uri = getIntent().getData(); - /*int eventCount = IcalendarUtils.getEventCountFromCalendarFile(this, uri); - if (eventCount == 1) { - Intent calIntent = new Intent(Intent.ACTION_INSERT); - calIntent.setType("vnd.android.cursor.item/event"); - calIntent.putExtra("FILE_URI", uri); - startActivity(insertMultipleEventIntent); - } else if (eventCount > 1) { - Intent insertMultipleEventIntent = Intent(Intent.ACTION_INSERT_MULTIPLE); // to insert multiple file - insertMultipleEventIntent.putExtra("File_URI", uri) - startActivity(insertMultipleEventIntent); - } else { - showErrorToast(); - - } - finish(); - */ - VCalendar calendar = IcalendarUtils.readCalendarFromFile(this, uri); if (calendar == null) { @@ -168,83 +144,71 @@ public class ImportActivity extends Activity { return; } - final LinkedList events = calendar.getAllEvents(); - - if (events == null || events.isEmpty()) { + LinkedList events = calendar.getAllEvents(); + if (events == null) { showErrorToast(); return; } - if (events.size() == 1) { - Log.d("Vincent", "Try to insert a single event"); - VEvent event = calendar.getAllEvents().getFirst(); - startInsertEventActivity(event); + if (events.size() == 1){ + handleSingleEvent(calendar); } else { - final Intent calIntent = new Intent(Intent.ACTION_INSERT); - calIntent.setType("vnd.android.cursor.item/event"); - - final ArrayList bundleList = new ArrayList<>(); - for (VEvent event : events) { - bundleList.add(putEventInBundle(event)); - } - - calIntent.putParcelableArrayListExtra("FAHIM", bundleList); - startActivity(calIntent); - finish(); - //displayPickCalendarDialog(); + handleMultipleEvents(events); } } - private void displayPickCalendarDialog() { - final FragmentManager fragmentManager = getFragmentManager(); - if (fragmentManager != null) { - //setContentView(R.layout.import_activity); - Log.d("Vincent", "instanciate pickCalendarDialogFragment"); - //mPickCalendarDialog = new PickCalendarDialogFragment(); - Log.d("Vincent", "display Pick calendar dialog"); - //mPickCalendarDialog.show(fragmentManager, PickCalendarDialogFragment.FRAGMENT_TAG); + private void handleMultipleEvents(List events) { + Intent intent = new Intent(this, AllInOneActivity.class); + ArrayList bundleList = new ArrayList<>(); - } else { - Log.d("Vincent", "fragmentManager is null"); + 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"); - private Bundle putEventInBundle(VEvent event) { - 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) { + VEvent firstEvent = calendar.getAllEvents().getFirst(); + calIntent.putExtra(CalendarContract.Events.TITLE, + IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.SUMMARY))); + calIntent.putExtra(CalendarContract.Events.EVENT_LOCATION, + IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.LOCATION))); + calIntent.putExtra(CalendarContract.Events.DESCRIPTION, + IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.DESCRIPTION))); + calIntent.putExtra(ExtendedProperty.URL, + IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.URL))); + calIntent.putExtra(CalendarContract.Events.ORGANIZER, + IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.ORGANIZER))); + calIntent.putExtra(CalendarContract.Events.RRULE, + IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.RRULE))); + + if (firstEvent.mAttendees.size() > 0) { StringBuilder builder = new StringBuilder(); - for (Attendee attendee : event.mAttendees) { + for (Attendee attendee : firstEvent.mAttendees) { builder.append(attendee.mEmail); builder.append(","); } - bundle.putString(Intent.EXTRA_EMAIL, builder.toString()); + calIntent.putExtra(Intent.EXTRA_EMAIL, builder.toString()); } - String dtStart = event.getProperty(VEvent.DTSTART); - String dtStartParam = event.getPropertyParameters(VEvent.DTSTART); + String dtStart = firstEvent.getProperty(VEvent.DTSTART); + String dtStartParam = firstEvent.getPropertyParameters(VEvent.DTSTART); if (!TextUtils.isEmpty(dtStart)) { - bundle.putLong(CalendarContract.EXTRA_EVENT_BEGIN_TIME, + calIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, getLocalTimeFromString(dtStart, dtStartParam)); } - String dtEnd = event.getProperty(VEvent.DTEND); - String dtEndParam = event.getPropertyParameters(VEvent.DTEND); + String dtEnd = firstEvent.getProperty(VEvent.DTEND); + String dtEndParam = firstEvent.getPropertyParameters(VEvent.DTEND); if (dtEnd != null && !TextUtils.isEmpty(dtEnd)) { - bundle.putLong(CalendarContract.EXTRA_EVENT_END_TIME, + calIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, getLocalTimeFromString(dtEnd, dtEndParam)); } else { // Treat start date as end date if un-specified @@ -257,26 +221,17 @@ public class ImportActivity extends Activity { if (isTimeStartOfDay(dtStart, dtStartParam)) { - bundle.putBoolean(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay); + calIntent.putExtra(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); + String microsoft_all_day_event = firstEvent.getProperty("X-MICROSOFT-CDO-ALLDAYEVENT"); + if(!TextUtils.isEmpty(microsoft_all_day_event) && microsoft_all_day_event.equals("TRUE")){ + calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, true); } - bundle.putBoolean(EditEventActivity.EXTRA_READ_ONLY, true); - - return bundle; - } - - private void startInsertEventActivity(VEvent event) { - Intent calIntent = new Intent(Intent.ACTION_INSERT); - calIntent.setType("vnd.android.cursor.item/event"); - Bundle bundle = putEventInBundle(event); - calIntent.putExtra("bundle", bundle); + calIntent.putExtra(EditEventActivity.EXTRA_READ_ONLY, true); try { startActivity(calIntent); diff --git a/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java similarity index 89% rename from app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java rename to app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index 0efe7564d..bc0f23ad9 100644 --- a/app/src/main/java/com/android/calendar/event/PickCalendarDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -4,20 +4,21 @@ package com.android.calendar.event; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; - -import android.app.DialogFragment; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.Spinner; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; + import ws.xsoh.etar.R; /** * Allows the user to quickly import a multi event cal file. */ -public class PickCalendarDialogFragment extends DialogFragment { +public class CalendarPickerDialogFragment extends DialogFragment { private static final String TAG = "PickCalendarDialogFragment"; public static final String FRAGMENT_TAG = "PICK_CALENDAR_DIALOG"; @@ -25,11 +26,12 @@ public class PickCalendarDialogFragment extends DialogFragment { private AlertDialog mAlertDialog; Spinner mCalendarsSpinner; - public PickCalendarDialogFragment() { + public CalendarPickerDialogFragment() { // Empty constructor required for DialogFragment. } + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Activity activity = getActivity(); diff --git a/app/src/main/java/com/android/calendar/event/EditEventActivity.java b/app/src/main/java/com/android/calendar/event/EditEventActivity.java index 438559d94..2a5dbb107 100644 --- a/app/src/main/java/com/android/calendar/event/EditEventActivity.java +++ b/app/src/main/java/com/android/calendar/event/EditEventActivity.java @@ -23,11 +23,9 @@ import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.os.Parcelable; import android.provider.CalendarContract.Events; import android.util.Log; import android.view.MenuItem; -import android.widget.Toast; import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.Toolbar; @@ -39,10 +37,8 @@ import com.android.calendar.CalendarController.EventInfo; import com.android.calendar.CalendarEventModel.ReminderEntry; import com.android.calendar.DynamicTheme; import com.android.calendar.Utils; -import com.android.calendar.icalendar.IcalendarUtils; -import com.android.calendar.icalendar.VEvent; import com.android.calendarcommon2.Time; -import com.android.calendar.icalendar.VCalendar; + import java.util.ArrayList; import ws.xsoh.etar.R; @@ -65,8 +61,6 @@ public class EditEventActivity extends AbstractCalendarActivity { private boolean mEventColorInitialized; - private ArrayList mEventInfos; - private EventInfo mEventInfo; @Override @@ -75,57 +69,49 @@ public class EditEventActivity extends AbstractCalendarActivity { dynamicTheme.onCreate(this); setContentView(R.layout.simple_frame_layout_material); + mEventInfo = getEventInfoFromIntent(icicle); + mReminders = getReminderEntriesFromIntent(); + mEventColorInitialized = getIntent().hasExtra(EXTRA_EVENT_COLOR); + mEventColor = getIntent().getIntExtra(EXTRA_EVENT_COLOR, -1); + Toolbar myToolbar = findViewById(R.id.toolbar); + setSupportActionBar(myToolbar); + + mEditFragment = (EditEventFragment) getSupportFragmentManager().findFragmentById(R.id.body_frame); + + mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config); + + if (mIsMultipane) { + getSupportActionBar().setDisplayOptions( + ActionBar.DISPLAY_SHOW_TITLE, + ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME + | ActionBar.DISPLAY_SHOW_TITLE); + getSupportActionBar().setTitle( + mEventInfo.id == -1 ? R.string.event_create : R.string.event_edit); + } + else { + getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, + ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME| + ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM); + } - mEventInfos = getEventInfoFromIntent(icicle); - - - if (mEventInfos.size() == 1) { - mEventInfo = mEventInfos.get(0); //get first - mReminders = getReminderEntriesFromIntent(); - mEventColorInitialized = getIntent().hasExtra(EXTRA_EVENT_COLOR); - mEventColor = getIntent().getIntExtra(EXTRA_EVENT_COLOR, -1); - Toolbar myToolbar = findViewById(R.id.toolbar); - setSupportActionBar(myToolbar); - - mEditFragment = (EditEventFragment) getSupportFragmentManager().findFragmentById(R.id.body_frame); - - mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config); - - if (mIsMultipane) { - getSupportActionBar().setDisplayOptions( - ActionBar.DISPLAY_SHOW_TITLE, - ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME - | ActionBar.DISPLAY_SHOW_TITLE); - getSupportActionBar().setTitle( - mEventInfo.id == -1 ? R.string.event_create : R.string.event_edit); + if (mEditFragment == null) { + Intent intent = null; + boolean readOnly = false; + if (mEventInfo.id == -1) { + intent = getIntent(); + readOnly = intent.getBooleanExtra(EXTRA_READ_ONLY, false); } - else { - getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, - ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME| - ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM); - } - - if (mEditFragment == null) { - Intent intent = null; - boolean readOnly = false; - if (mEventInfo.id == -1) { - intent = getIntent(); - readOnly = intent.getBooleanExtra(EXTRA_READ_ONLY, false); - } - mEditFragment = new EditEventFragment(mEventInfo, mReminders, mEventColorInitialized, - mEventColor, readOnly, intent); + mEditFragment = new EditEventFragment(mEventInfo, mReminders, mEventColorInitialized, + mEventColor, readOnly, intent); - mEditFragment.mShowModifyDialogOnLaunch = getIntent().getBooleanExtra( - CalendarController.EVENT_EDIT_ON_LAUNCH, false); + mEditFragment.mShowModifyDialogOnLaunch = getIntent().getBooleanExtra( + CalendarController.EVENT_EDIT_ON_LAUNCH, false); - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - ft.replace(R.id.body_frame, mEditFragment); - ft.show(mEditFragment); - ft.commit(); - } - } else if(mEventInfos.size() > 1) { - Toast.makeText(this, "Try to import " + mEventInfos.size() + "Bundle", Toast.LENGTH_SHORT).show(); + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + ft.replace(R.id.body_frame, mEditFragment); + ft.show(mEditFragment); + ft.commit(); } } @@ -135,69 +121,51 @@ public class EditEventActivity extends AbstractCalendarActivity { return (ArrayList) intent.getSerializableExtra(EXTRA_EVENT_REMINDERS); } - private ArrayList getEventInfoFromIntent(Bundle icicle) { - ArrayList eventInfos = new ArrayList<>(); - - final Intent intent = getIntent(); - - ArrayList bundles = intent.getParcelableArrayListExtra("FAHIM"); - - if (bundles == null) return eventInfos; - - 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"); - } + private EventInfo getEventInfoFromIntent(Bundle icicle) { + EventInfo info = new EventInfo(); + long eventId = -1; + Intent intent = getIntent(); + 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); } - - final EventInfo eventInfo = loadEventInfoFromBundle(eventId, bundle); - eventInfos.add(eventInfo); + } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) { + eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID); } - return eventInfos; - } - - private EventInfo loadEventInfoFromBundle(long eventId, Bundle bundle) { - EventInfo eventInfo = new EventInfo(); + boolean allDay = intent.getBooleanExtra(EXTRA_EVENT_ALL_DAY, false); - boolean allDay = bundle.getBoolean(EXTRA_EVENT_ALL_DAY, false); - - long begin = bundle.getLong(EXTRA_EVENT_BEGIN_TIME, -1); - long end = bundle.getLong(EXTRA_EVENT_END_TIME, -1); + long begin = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1); + long end = intent.getLongExtra(EXTRA_EVENT_END_TIME, -1); if (end != -1) { - eventInfo.endTime = new Time(); + info.endTime = new Time(); if (allDay) { - eventInfo.endTime.setTimezone(Time.TIMEZONE_UTC); + info.endTime.setTimezone(Time.TIMEZONE_UTC); } - eventInfo.endTime.set(end); + info.endTime.set(end); } if (begin != -1) { - eventInfo.startTime = new Time(); + info.startTime = new Time(); if (allDay) { - eventInfo.startTime.setTimezone(Time.TIMEZONE_UTC); + info.startTime.setTimezone(Time.TIMEZONE_UTC); } - eventInfo.startTime.set(begin); + info.startTime.set(begin); } - eventInfo.id = eventId; - eventInfo.eventTitle = bundle.getString(Events.TITLE); - eventInfo.calendarId = bundle.getLong(Events.CALENDAR_ID, -1); + info.id = eventId; + info.eventTitle = intent.getStringExtra(Events.TITLE); + info.calendarId = intent.getLongExtra(Events.CALENDAR_ID, -1); if (allDay) { - eventInfo.extraLong = CalendarController.EXTRA_CREATE_ALL_DAY; + info.extraLong = CalendarController.EXTRA_CREATE_ALL_DAY; } else { - eventInfo.extraLong = 0; + info.extraLong = 0; } - - return eventInfo; + return info; } @Override -- GitLab From a93aaf1f79df7e748405374ea83c6c04bfaf6bd5 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 19 Aug 2024 15:25:38 +0200 Subject: [PATCH 11/45] task: pushed changes at state when Fahim left on August 19th --- .../event/CalendarPickerDialogFragment.java | 135 +++++++++++++++++- .../calendar/event/EditEventFragment.java | 1 + 2 files changed, 134 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index bc0f23ad9..55c7415fd 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -1,18 +1,33 @@ package com.android.calendar.event; +import static ws.xsoh.etar.BuildConfig.DEBUG; + import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; import android.content.Context; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; import android.os.Bundle; +import android.provider.CalendarContract; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.Spinner; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.android.calendar.CalendarEventModel; +import com.android.calendar.Utils; +import com.android.calendar.settings.GeneralPreferences; + import ws.xsoh.etar.R; /** @@ -20,24 +35,32 @@ import ws.xsoh.etar.R; */ public class CalendarPickerDialogFragment extends DialogFragment { - private static final String TAG = "PickCalendarDialogFragment"; + private static final String TAG = CalendarPickerDialogFragment.class.getName(); public static final String FRAGMENT_TAG = "PICK_CALENDAR_DIALOG"; + private static final int TOKEN_CALENDARS = 8; + private CalendarQueryHandler mCalendarQueryHandler; + public long mPickedCalendarId = -1; + private CalendarEventModel mCalendarModel; private AlertDialog mAlertDialog; + private Spinner mCalendarsSpinner; public CalendarPickerDialogFragment() { // Empty constructor required for DialogFragment. } - @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Activity activity = getActivity(); + //TODO check activity is not null + final LayoutInflater layoutInflater = (LayoutInflater) activity .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + initiateCalendarQueryHandler(activity); + final View view = layoutInflater.inflate(R.layout.pick_calendar_dialog, null); mCalendarsSpinner = (Spinner) view.findViewById(R.id.calendars_spinner); @@ -57,4 +80,112 @@ public class CalendarPickerDialogFragment extends DialogFragment { } + private void initiateCalendarQueryHandler(@NonNull Activity activity) { + final ContentResolver contentResolver = activity.getContentResolver(); + if (contentResolver == null) { + return ; // TODO handle properly this case + } + + mCalendarQueryHandler = new CalendarQueryHandler(contentResolver); + + Log.d("FAHIM", "onCreateView call()"); + mCalendarQueryHandler.startQuery(TOKEN_CALENDARS, null, CalendarContract.Calendars.CONTENT_URI, + EditEventHelper.CALENDARS_PROJECTION, + EditEventHelper.CALENDARS_WHERE_WRITEABLE_VISIBLE, null /* selection args */, + null /* sort order */); + + } + + + public class CalendarQueryHandler extends AsyncQueryHandler { + + public CalendarQueryHandler(ContentResolver cr) { + super(cr); + } + + @Override + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + super.onQueryComplete(token, cookie, cursor); + + + // If the query didn't return a cursor for some reason return + if (cursor == null) { + return; + } + + Log.d("FAHIM", "token is: "+token+", cursor: "+cursor.getCount()); + + // If the Activity is finishing, then close the cursor. + // Otherwise, use the new cursor in the adapter. + final Activity activity = CalendarPickerDialogFragment.this.getActivity(); + if (activity == null || activity.isFinishing()) { + cursor.close(); + return; + } + + try { + MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); + if (DEBUG) { + Log.d("FAHIM", "onQueryComplete: setting cursor with " + + matrixCursor.getCount() + " calendars"); + } + + final long selectedCalendarId = mPickedCalendarId; + //mView.setCalendarsCursor(matrixCursor, isAdded() && isResumed(), + // selectedCalendarId); + + int selection; + selection = findDefaultCalendarPosition(cursor); + + Log.d("FAHIM", "selection is: "+selection); + + EditEventView.CalendarsAdapter adapter = new EditEventView.CalendarsAdapter(activity, + R.layout.calendars_spinner_item, cursor); + mCalendarsSpinner.setAdapter(adapter); + //mCalendarsSpinner.setOnItemSelectedListener(this); + mCalendarsSpinner.setSelection(selection); + + } finally { + cursor.close(); + } + //setModelIfDone(TOKEN_CALENDARS); + } + } + + private int findDefaultCalendarPosition(Cursor calendarsCursor) { + if (calendarsCursor.getCount() <= 0) { + return -1; + } + + String defaultCalendar = Utils.getSharedPreference( + getContext(), GeneralPreferences.KEY_DEFAULT_CALENDAR, (String) null); + + int calendarsOwnerIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.OWNER_ACCOUNT); + int calendarNameIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME); + int accountNameIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.ACCOUNT_NAME); + int accountTypeIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.ACCOUNT_TYPE); + int position = 0; + calendarsCursor.moveToPosition(-1); + while (calendarsCursor.moveToNext()) { + String calendarOwner = calendarsCursor.getString(calendarsOwnerIndex); + String calendarName = calendarsCursor.getString(calendarNameIndex); + String currentCalendar = calendarOwner + "/" + calendarName; + if (defaultCalendar == null) { + // There is no stored default upon the first time running. Use a primary + // calendar in this case. + if (calendarOwner != null && + calendarOwner.equals(calendarsCursor.getString(accountNameIndex)) && + !CalendarContract.ACCOUNT_TYPE_LOCAL.equals( + calendarsCursor.getString(accountTypeIndex))) { + return position; + } + } else if (defaultCalendar.equals(currentCalendar)) { + // Found the default calendar. + return position; + } + position++; + } + return 0; + } } + diff --git a/app/src/main/java/com/android/calendar/event/EditEventFragment.java b/app/src/main/java/com/android/calendar/event/EditEventFragment.java index 8f00a76e0..979af7fd3 100644 --- a/app/src/main/java/com/android/calendar/event/EditEventFragment.java +++ b/app/src/main/java/com/android/calendar/event/EditEventFragment.java @@ -758,6 +758,7 @@ public class EditEventFragment extends Fragment implements EventHandler, OnColor EditEventHelper.CALENDARS_WHERE_WRITEABLE_VISIBLE, null /* selection args */, null /* sort order */); + // TOKEN_COLORS mHandler.startQuery(TOKEN_COLORS, null, Colors.CONTENT_URI, EditEventHelper.COLORS_PROJECTION, -- GitLab From 7c7931c797bfb74af829cdb58fd715fea3d0aaae Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 19 Aug 2024 17:22:17 +0200 Subject: [PATCH 12/45] feature: get calendar selected ID when pop up is closed by clicking on positive button tried to make the spinner to display calendar items but fails so added debug instead and moved some code outside of CalendarQueryHandler in CalendarPickerDialog --- .../android/calendar/AllInOneActivity.java | 6 +- .../event/CalendarPickerDialogFragment.java | 175 ++++++++++++------ 2 files changed, 127 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 6d56d35dc..bc90e57d6 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -246,7 +246,11 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH fragment.dismiss(); } - fragment = new CalendarPickerDialogFragment(); + CalendarPickerDialogFragment.CalendarPickerDialogListener listener = selectedCalendarId -> { + Log.d("FAHIM", "Selected calendar id is: "+selectedCalendarId); + //todo import all event from .ics file into this calendar + }; + fragment = new CalendarPickerDialogFragment(listener); fragment.show(fragmentManager, CalendarPickerDialogFragment.FRAGMENT_TAG); // FIXME: 19/08/2024 Remove when implementation done diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index 55c7415fd..da8bcf7c4 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -18,7 +18,9 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; import android.widget.Spinner; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -33,21 +35,25 @@ import ws.xsoh.etar.R; /** * Allows the user to quickly import a multi event cal file. */ -public class CalendarPickerDialogFragment extends DialogFragment { +public class CalendarPickerDialogFragment extends DialogFragment implements AdapterView.OnItemSelectedListener { private static final String TAG = CalendarPickerDialogFragment.class.getName(); public static final String FRAGMENT_TAG = "PICK_CALENDAR_DIALOG"; private static final int TOKEN_CALENDARS = 8; private CalendarQueryHandler mCalendarQueryHandler; - public long mPickedCalendarId = -1; - private CalendarEventModel mCalendarModel; + private long mPickedCalendarId = -1; + private AlertDialog mAlertDialog; - private - Spinner mCalendarsSpinner; + private Cursor mCalendarsCursor; + private Spinner mCalendarsSpinner; + + private CalendarPickerDialogListener mListener; //instance of class that request to show this dialog and which expect the selected calendar + - public CalendarPickerDialogFragment() { + public CalendarPickerDialogFragment(CalendarPickerDialogListener listener) { // Empty constructor required for DialogFragment. + mListener = listener; } @NonNull @@ -70,7 +76,7 @@ public class CalendarPickerDialogFragment extends DialogFragment { .setView(view) .setPositiveButton(R.string.pick_calendar_dialog_validate, (dialog, which) -> { - //todo may be it could be better to have the listener define in importActivity + mListener.onCalendarPicked(mPickedCalendarId); dismiss(); }) .setNegativeButton(android.R.string.cancel, null) @@ -96,6 +102,111 @@ public class CalendarPickerDialogFragment extends DialogFragment { } + public void setCalendarsCursor(Cursor cursor, boolean userVisible) { + // If there are no syncable calendars, then we cannot allow + // creating a new event. + mCalendarsCursor = cursor; + if (cursor == null || cursor.getCount() == 0) { + // Cancel the "loading calendars" dialog if it exists + /*if (mSaveAfterQueryComplete) { + mLoadingCalendarsDialog.cancel(); + }*/ + if (!userVisible) { + return; + } + // Create an error message for the user that, when clicked, + // will exit this activity without saving the event. + //TODO display a message to indicate to add a calendar if there is none + Toast.makeText(getContext(), R.string.no_calendars_found, Toast.LENGTH_SHORT).show(); + return; + } + + int selection = findDefaultCalendarPosition(cursor); + mPickedCalendarId = selection; + Log.d("FAHIM", "Got the default calendar: "+selection); + // populate the calendars spinner + EditEventView.CalendarsAdapter adapter = new EditEventView.CalendarsAdapter(getContext(), + R.layout.calendars_spinner_item, cursor); + + Log.d("FAHIM", "Created the adapter"); + mCalendarsSpinner.setAdapter(adapter); + Log.d("FAHIM", "set the adapter"); + mCalendarsSpinner.setOnItemSelectedListener(this); + Log.d("FAHIM", "setOnItemSelectedListener"); + mCalendarsSpinner.setSelection(selection); + Log.d("FAHIM", "setSelection to:"+selection); + + /** + * TODO do something with below code + * if (mSaveAfterQueryComplete) { + mLoadingCalendarsDialog.cancel(); + if (prepareForSave() && fillModelFromUI()) { + int exit = userVisible ? Utils.DONE_EXIT : 0; + mDone.setDoneCode(Utils.DONE_SAVE | exit); + mDone.run(); + } else if (userVisible) { + mDone.setDoneCode(Utils.DONE_EXIT); + mDone.run(); + } else if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "SetCalendarsCursor:Save failed and unable to exit view"); + } + return; + }**/ + } + + private int findDefaultCalendarPosition(Cursor calendarsCursor) { + if (calendarsCursor.getCount() <= 0) { + return -1; + } + + String defaultCalendar = Utils.getSharedPreference( + getContext(), GeneralPreferences.KEY_DEFAULT_CALENDAR, (String) null); + Log.d("FAHIM", "findDefaultCalendarPosition with default: "+defaultCalendar); + + int calendarsOwnerIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.OWNER_ACCOUNT); + int calendarNameIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME); + int accountNameIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.ACCOUNT_NAME); + int accountTypeIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.ACCOUNT_TYPE); + int position = 0; + calendarsCursor.moveToPosition(-1); + Log.d("FAHIM", "findDefaultCalendarPosition loop through calendar's cursor. Position : 0"); + while (calendarsCursor.moveToNext()) { + Log.d("FAHIM", "findDefaultCalendarPosition calendarcursor move to Position : "+position); + String calendarOwner = calendarsCursor.getString(calendarsOwnerIndex); + String calendarName = calendarsCursor.getString(calendarNameIndex); + String currentCalendar = calendarOwner + "/" + calendarName; + if (defaultCalendar == null) { + // There is no stored default upon the first time running. Use a primary + // calendar in this case. + if (calendarOwner != null && + calendarOwner.equals(calendarsCursor.getString(accountNameIndex)) && + !CalendarContract.ACCOUNT_TYPE_LOCAL.equals( + calendarsCursor.getString(accountTypeIndex))) { + Log.d("FAHIM", "use primary claendar as default"); + return position; + } + } else if (defaultCalendar.equals(currentCalendar)) { + // Found the default calendar. + Log.d("FAHIM", "use default calendar"); + return position; + } + position++; + } + return 0; + } + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + Log.d("FAHIM", "onItemSelected.id: "+id); + //todo inspire from EditEventView.onItemSelected line 1503 + mPickedCalendarId = id; + } + + @Override + public void onNothingSelected(AdapterView parent) { + Log.d("FAHIM", "onNothingSelected"); + } + public class CalendarQueryHandler extends AsyncQueryHandler { @@ -107,7 +218,6 @@ public class CalendarPickerDialogFragment extends DialogFragment { protected void onQueryComplete(int token, Object cookie, Cursor cursor) { super.onQueryComplete(token, cookie, cursor); - // If the query didn't return a cursor for some reason return if (cursor == null) { return; @@ -119,6 +229,7 @@ public class CalendarPickerDialogFragment extends DialogFragment { // Otherwise, use the new cursor in the adapter. final Activity activity = CalendarPickerDialogFragment.this.getActivity(); if (activity == null || activity.isFinishing()) { + Log.d("FAHIM", "activity is null or finishing"); cursor.close(); return; } @@ -129,63 +240,21 @@ public class CalendarPickerDialogFragment extends DialogFragment { Log.d("FAHIM", "onQueryComplete: setting cursor with " + matrixCursor.getCount() + " calendars"); } - - final long selectedCalendarId = mPickedCalendarId; - //mView.setCalendarsCursor(matrixCursor, isAdded() && isResumed(), - // selectedCalendarId); - int selection; selection = findDefaultCalendarPosition(cursor); Log.d("FAHIM", "selection is: "+selection); - - EditEventView.CalendarsAdapter adapter = new EditEventView.CalendarsAdapter(activity, - R.layout.calendars_spinner_item, cursor); - mCalendarsSpinner.setAdapter(adapter); - //mCalendarsSpinner.setOnItemSelectedListener(this); - mCalendarsSpinner.setSelection(selection); + CalendarPickerDialogFragment.this.setCalendarsCursor(cursor, true); } finally { cursor.close(); } - //setModelIfDone(TOKEN_CALENDARS); } } - private int findDefaultCalendarPosition(Cursor calendarsCursor) { - if (calendarsCursor.getCount() <= 0) { - return -1; - } - String defaultCalendar = Utils.getSharedPreference( - getContext(), GeneralPreferences.KEY_DEFAULT_CALENDAR, (String) null); - - int calendarsOwnerIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.OWNER_ACCOUNT); - int calendarNameIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME); - int accountNameIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.ACCOUNT_NAME); - int accountTypeIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.ACCOUNT_TYPE); - int position = 0; - calendarsCursor.moveToPosition(-1); - while (calendarsCursor.moveToNext()) { - String calendarOwner = calendarsCursor.getString(calendarsOwnerIndex); - String calendarName = calendarsCursor.getString(calendarNameIndex); - String currentCalendar = calendarOwner + "/" + calendarName; - if (defaultCalendar == null) { - // There is no stored default upon the first time running. Use a primary - // calendar in this case. - if (calendarOwner != null && - calendarOwner.equals(calendarsCursor.getString(accountNameIndex)) && - !CalendarContract.ACCOUNT_TYPE_LOCAL.equals( - calendarsCursor.getString(accountTypeIndex))) { - return position; - } - } else if (defaultCalendar.equals(currentCalendar)) { - // Found the default calendar. - return position; - } - position++; - } - return 0; + public interface CalendarPickerDialogListener { + public void onCalendarPicked(long selectedCalendarId); } } -- GitLab From c59db43f66c278e5392b6f4e48830ef2eeb0d438 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 19 Aug 2024 17:24:15 +0200 Subject: [PATCH 13/45] task: add debug logging in EditEventView.java --- app/src/main/java/com/android/calendar/event/EditEventView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/android/calendar/event/EditEventView.java b/app/src/main/java/com/android/calendar/event/EditEventView.java index c001c8b96..98bc65d16 100644 --- a/app/src/main/java/com/android/calendar/event/EditEventView.java +++ b/app/src/main/java/com/android/calendar/event/EditEventView.java @@ -1632,11 +1632,13 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa public static class CalendarsAdapter extends ResourceCursorAdapter { public CalendarsAdapter(Context context, int resourceId, Cursor c) { super(context, resourceId, c); + Log.d("FAHIM", "Call CalendarsAdapter's constructor"); setDropDownViewResource(R.layout.calendars_dropdown_item); } @Override public void bindView(View view, Context context, Cursor cursor) { + Log.d("FAHIM", "Call CalendarsAdapter's bindView with cursor: "+cursor.getCount()); View colorBar = view.findViewById(R.id.color); int colorColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR); int nameColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_DISPLAY_NAME); -- GitLab From c9fc9ec6c823f0c6d82676613272cd14ba38f182 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 20 Aug 2024 16:07:59 +0200 Subject: [PATCH 14/45] fix: make the Calendar picker dialog's spinner to display calendars and allow there selections --- .../event/CalendarPickerDialogFragment.java | 146 +++++++----------- 1 file changed, 57 insertions(+), 89 deletions(-) diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index da8bcf7c4..d5dcc3120 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -9,6 +9,7 @@ import android.app.Dialog; import android.content.AsyncQueryHandler; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; @@ -19,7 +20,10 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; +import android.widget.CursorAdapter; +import android.widget.ResourceCursorAdapter; import android.widget.Spinner; +import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; @@ -44,6 +48,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap private long mPickedCalendarId = -1; + private View mView; private AlertDialog mAlertDialog; private Cursor mCalendarsCursor; private Spinner mCalendarsSpinner; @@ -65,21 +70,25 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap final LayoutInflater layoutInflater = (LayoutInflater) activity .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - initiateCalendarQueryHandler(activity); - - final View view = layoutInflater.inflate(R.layout.pick_calendar_dialog, null); - mCalendarsSpinner = (Spinner) view.findViewById(R.id.calendars_spinner); + initiateCalendarQueryHandler(getActivity()); + mView = layoutInflater.inflate(R.layout.pick_calendar_dialog, null); + mCalendarsSpinner = (Spinner) mView.findViewById(R.id.calendars_spinner); mAlertDialog = new AlertDialog.Builder(activity) .setTitle(R.string.pick_calendar_dialog_title) - .setView(view) + .setView(mView) .setPositiveButton(R.string.pick_calendar_dialog_validate, (dialog, which) -> { mListener.onCalendarPicked(mPickedCalendarId); dismiss(); }) - .setNegativeButton(android.R.string.cancel, null) + .setNegativeButton(android.R.string.cancel, (dialog, which) -> dismiss()) + .setOnDismissListener(dialog -> { + if (mCalendarsCursor != null && !mCalendarsCursor.isClosed()) { + mCalendarsCursor.close(); + } + }) .create(); return mAlertDialog; @@ -103,96 +112,25 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap } public void setCalendarsCursor(Cursor cursor, boolean userVisible) { - // If there are no syncable calendars, then we cannot allow - // creating a new event. mCalendarsCursor = cursor; if (cursor == null || cursor.getCount() == 0) { - // Cancel the "loading calendars" dialog if it exists - /*if (mSaveAfterQueryComplete) { - mLoadingCalendarsDialog.cancel(); - }*/ if (!userVisible) { return; } - // Create an error message for the user that, when clicked, - // will exit this activity without saving the event. - //TODO display a message to indicate to add a calendar if there is none Toast.makeText(getContext(), R.string.no_calendars_found, Toast.LENGTH_SHORT).show(); return; } - int selection = findDefaultCalendarPosition(cursor); - mPickedCalendarId = selection; - Log.d("FAHIM", "Got the default calendar: "+selection); // populate the calendars spinner - EditEventView.CalendarsAdapter adapter = new EditEventView.CalendarsAdapter(getContext(), - R.layout.calendars_spinner_item, cursor); + CalPickerAdapter adapter = new CalPickerAdapter(getContext(), mCalendarsCursor); + //adapter.setDropDownViewResource(R.layout.calendars_dropdown_item); + Log.d("FAHIM", "Created the adapter"); mCalendarsSpinner.setAdapter(adapter); Log.d("FAHIM", "set the adapter"); mCalendarsSpinner.setOnItemSelectedListener(this); Log.d("FAHIM", "setOnItemSelectedListener"); - mCalendarsSpinner.setSelection(selection); - Log.d("FAHIM", "setSelection to:"+selection); - - /** - * TODO do something with below code - * if (mSaveAfterQueryComplete) { - mLoadingCalendarsDialog.cancel(); - if (prepareForSave() && fillModelFromUI()) { - int exit = userVisible ? Utils.DONE_EXIT : 0; - mDone.setDoneCode(Utils.DONE_SAVE | exit); - mDone.run(); - } else if (userVisible) { - mDone.setDoneCode(Utils.DONE_EXIT); - mDone.run(); - } else if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "SetCalendarsCursor:Save failed and unable to exit view"); - } - return; - }**/ - } - - private int findDefaultCalendarPosition(Cursor calendarsCursor) { - if (calendarsCursor.getCount() <= 0) { - return -1; - } - - String defaultCalendar = Utils.getSharedPreference( - getContext(), GeneralPreferences.KEY_DEFAULT_CALENDAR, (String) null); - Log.d("FAHIM", "findDefaultCalendarPosition with default: "+defaultCalendar); - - int calendarsOwnerIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.OWNER_ACCOUNT); - int calendarNameIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME); - int accountNameIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.ACCOUNT_NAME); - int accountTypeIndex = calendarsCursor.getColumnIndexOrThrow(CalendarContract.Calendars.ACCOUNT_TYPE); - int position = 0; - calendarsCursor.moveToPosition(-1); - Log.d("FAHIM", "findDefaultCalendarPosition loop through calendar's cursor. Position : 0"); - while (calendarsCursor.moveToNext()) { - Log.d("FAHIM", "findDefaultCalendarPosition calendarcursor move to Position : "+position); - String calendarOwner = calendarsCursor.getString(calendarsOwnerIndex); - String calendarName = calendarsCursor.getString(calendarNameIndex); - String currentCalendar = calendarOwner + "/" + calendarName; - if (defaultCalendar == null) { - // There is no stored default upon the first time running. Use a primary - // calendar in this case. - if (calendarOwner != null && - calendarOwner.equals(calendarsCursor.getString(accountNameIndex)) && - !CalendarContract.ACCOUNT_TYPE_LOCAL.equals( - calendarsCursor.getString(accountTypeIndex))) { - Log.d("FAHIM", "use primary claendar as default"); - return position; - } - } else if (defaultCalendar.equals(currentCalendar)) { - // Found the default calendar. - Log.d("FAHIM", "use default calendar"); - return position; - } - position++; - } - return 0; } @Override @@ -205,6 +143,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap @Override public void onNothingSelected(AdapterView parent) { Log.d("FAHIM", "onNothingSelected"); + mPickedCalendarId = -1; } @@ -225,12 +164,10 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap Log.d("FAHIM", "token is: "+token+", cursor: "+cursor.getCount()); - // If the Activity is finishing, then close the cursor. - // Otherwise, use the new cursor in the adapter. final Activity activity = CalendarPickerDialogFragment.this.getActivity(); if (activity == null || activity.isFinishing()) { Log.d("FAHIM", "activity is null or finishing"); - cursor.close(); + //cursor.close(); return; } @@ -240,21 +177,52 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap Log.d("FAHIM", "onQueryComplete: setting cursor with " + matrixCursor.getCount() + " calendars"); } - int selection; - selection = findDefaultCalendarPosition(cursor); - - Log.d("FAHIM", "selection is: "+selection); CalendarPickerDialogFragment.this.setCalendarsCursor(cursor, true); } finally { - cursor.close(); + //cursor.close(); } } } - public interface CalendarPickerDialogListener { public void onCalendarPicked(long selectedCalendarId); } + + public static class CalPickerAdapter extends CursorAdapter { + private LayoutInflater inflater; + public CalPickerAdapter(Context context, Cursor cursor) { + super(context, cursor, 0); //No selection flags added to avoid deprecated call + inflater = LayoutInflater.from(context); + Log.d("FAHIM", "Call CalendarsAdapter's constructor"); + + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + Log.d("FAHIM", "calling newView"); + return inflater.inflate(R.layout.calendars_spinner_item, parent, false); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + Log.d("FAHIM", "Call CalendarsAdapter's bindView with cursor: "+cursor.getCount()); + + int nameColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME); + int ownerColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.OWNER_ACCOUNT); + + TextView name = (TextView) view.findViewById(R.id.calendar_name); + if (name != null) { + String displayName = cursor.getString(nameColumn); + name.setText(displayName); + + TextView accountName = (TextView) view.findViewById(R.id.account_name); + if (accountName != null) { + accountName.setText(cursor.getString(ownerColumn)); + accountName.setVisibility(TextView.VISIBLE); + } + } + } + } } -- GitLab From 177b179d8fa9aaa4268c81637b356ee6c282d08c Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 20 Aug 2024 16:53:17 +0200 Subject: [PATCH 15/45] fix: revert the CalendarPickerAdapter to extending ResourceCursorAdapter --- .../event/CalendarPickerDialogFragment.java | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index d5dcc3120..f2871da6a 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -9,30 +9,23 @@ import android.app.Dialog; import android.content.AsyncQueryHandler; import android.content.ContentResolver; import android.content.Context; -import android.content.DialogInterface; import android.database.Cursor; import android.database.MatrixCursor; -import android.net.Uri; import android.os.Bundle; import android.provider.CalendarContract; import android.util.Log; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.CursorAdapter; import android.widget.ResourceCursorAdapter; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; -import com.android.calendar.CalendarEventModel; import com.android.calendar.Utils; -import com.android.calendar.settings.GeneralPreferences; import ws.xsoh.etar.R; @@ -55,9 +48,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap private CalendarPickerDialogListener mListener; //instance of class that request to show this dialog and which expect the selected calendar - public CalendarPickerDialogFragment(CalendarPickerDialogListener listener) { - // Empty constructor required for DialogFragment. mListener = listener; } @@ -122,20 +113,15 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap } // populate the calendars spinner - CalPickerAdapter adapter = new CalPickerAdapter(getContext(), mCalendarsCursor); - //adapter.setDropDownViewResource(R.layout.calendars_dropdown_item); - + CalPickerAdapter adapter = new CalPickerAdapter(getContext(), R.layout.calendars_spinner_item, mCalendarsCursor); - Log.d("FAHIM", "Created the adapter"); mCalendarsSpinner.setAdapter(adapter); - Log.d("FAHIM", "set the adapter"); mCalendarsSpinner.setOnItemSelectedListener(this); - Log.d("FAHIM", "setOnItemSelectedListener"); } @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { - Log.d("FAHIM", "onItemSelected.id: "+id); + Log.d("FAHIM", "onItemSelected.id: "+id+", position: "+position); //todo inspire from EditEventView.onItemSelected line 1503 mPickedCalendarId = id; } @@ -167,7 +153,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap final Activity activity = CalendarPickerDialogFragment.this.getActivity(); if (activity == null || activity.isFinishing()) { Log.d("FAHIM", "activity is null or finishing"); - //cursor.close(); + cursor.close(); return; } @@ -177,10 +163,10 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap Log.d("FAHIM", "onQueryComplete: setting cursor with " + matrixCursor.getCount() + " calendars"); } - CalendarPickerDialogFragment.this.setCalendarsCursor(cursor, true); + CalendarPickerDialogFragment.this.setCalendarsCursor(matrixCursor, true); } finally { - //cursor.close(); + cursor.close(); } } } @@ -189,24 +175,22 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap public void onCalendarPicked(long selectedCalendarId); } - public static class CalPickerAdapter extends CursorAdapter { - private LayoutInflater inflater; - public CalPickerAdapter(Context context, Cursor cursor) { - super(context, cursor, 0); //No selection flags added to avoid deprecated call - inflater = LayoutInflater.from(context); - Log.d("FAHIM", "Call CalendarsAdapter's constructor"); - - } + public static class CalPickerAdapter extends ResourceCursorAdapter { - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - Log.d("FAHIM", "calling newView"); - return inflater.inflate(R.layout.calendars_spinner_item, parent, false); + public CalPickerAdapter(Context context, int resourceId, Cursor cursor) { + super(context, resourceId, cursor, 0); //No selection flags added to avoid deprecated call + setDropDownViewResource(R.layout.calendars_dropdown_item); } @Override public void bindView(View view, Context context, Cursor cursor) { Log.d("FAHIM", "Call CalendarsAdapter's bindView with cursor: "+cursor.getCount()); + View colorBar = view.findViewById(R.id.color); + int colorColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_COLOR); + if (colorBar != null) { + colorBar.setBackgroundColor(Utils.getDisplayColorFromColor(context, + cursor.getInt(colorColumn))); + } int nameColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME); int ownerColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.OWNER_ACCOUNT); -- GitLab From dd73147d5275a44faf8e293b119b5950d19c1a94 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 20 Aug 2024 17:57:03 +0200 Subject: [PATCH 16/45] feat(import.ics): Got CalendarEventModel from Bundle in AllinOneActivity instead of EventAction & try to save them in DB (but fails) --- .../android/calendar/AllInOneActivity.java | 38 ++++++++++++++----- .../java/com/android/calendar/EventUtils.java | 23 ++++++++++- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index bc90e57d6..664f7e68c 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -84,6 +84,7 @@ 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; @@ -237,7 +238,7 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH private AllInOneMenuExtensionsInterface mExtensions = ExtensionsFactory .getAllInOneMenuExtensions(); - private void showCalendarChooserDialog(List eventList) { + private void showCalendarChooserDialog(List eventList) { final FragmentManager fragmentManager = getSupportFragmentManager(); CalendarPickerDialogFragment fragment = ((CalendarPickerDialogFragment) fragmentManager.findFragmentByTag(CalendarPickerDialogFragment.FRAGMENT_TAG)); @@ -248,7 +249,25 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH CalendarPickerDialogFragment.CalendarPickerDialogListener listener = selectedCalendarId -> { Log.d("FAHIM", "Selected calendar id is: "+selectedCalendarId); - //todo import all event from .ics file into this calendar + + final EditEventHelper mEditEventHelper = new EditEventHelper(AllInOneActivity.this); + int addedEventcounter = 0; + for (CalendarEventModel event : eventList) { + //parse eventInfo to CalendarEventModel + + if( mEditEventHelper.saveEvent(event, null, 0, null)) { + addedEventcounter++; + } else { + // FIXME 20/08/2024 is it the expected behaviour ? check & update + // Replace raw string by value from res folder + Toast.makeText(getApplicationContext(), "Failed to add : "+event.mTitle+ "to the calendar", Toast.LENGTH_SHORT); + } + } + if (addedEventcounter > 0) { + // FIXME Replace raw string by value from res folder + Toast.makeText(getApplicationContext(), addedEventcounter + "events as been added to the calendar", Toast.LENGTH_SHORT); + } + }; fragment = new CalendarPickerDialogFragment(listener); fragment.show(fragmentManager, CalendarPickerDialogFragment.FRAGMENT_TAG); @@ -424,21 +443,22 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH } private void handleEvents(Bundle bundle, Intent intent) { - final List eventList = getEventList(bundle, intent); + final List eventList = getEventList(bundle, intent); if (eventList.isEmpty()) { return; } + // TODO: 19/08/2024 Decide to show dialog for consecutive .ics import. Now it replaces previous one. showCalendarChooserDialog(eventList); } - private List getEventList(Bundle icicle, Intent intent) { - List eventInfoList = new ArrayList<>(); + private List getEventList(Bundle icicle, Intent intent) { + final List eventModelList = new ArrayList<>(); final ArrayList bundles = intent.getParcelableArrayListExtra(AllInOneActivity.BUNDLE_KEY_MULTIPLE_EVENTS); if (bundles == null || bundles.isEmpty()) { - return eventInfoList; + return eventModelList; } for (Bundle bundle : bundles) { @@ -456,11 +476,11 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID); } - final EventInfo eventInfo = EventUtils.crateEventInfoFromBundle(eventId, bundle); - eventInfoList.add(eventInfo); + final CalendarEventModel eventModel = EventUtils.createEventModelFromBundle(eventId, bundle); + eventModelList.add(eventModel); } - return eventInfoList; + return eventModelList; } private void checkAppPermissions() { diff --git a/app/src/main/java/com/android/calendar/EventUtils.java b/app/src/main/java/com/android/calendar/EventUtils.java index 9d895aa2e..fc11991b6 100644 --- a/app/src/main/java/com/android/calendar/EventUtils.java +++ b/app/src/main/java/com/android/calendar/EventUtils.java @@ -37,6 +37,27 @@ public class EventUtils { private EventUtils() { } + + @NonNull + public static CalendarEventModel createEventModelFromBundle(long eventId, @NonNull Bundle bundle) { + CalendarEventModel model = new CalendarEventModel(); + model.mId = eventId; + model.mTitle = bundle.getString(CalendarContract.Events.TITLE, ""); + model.mLocation= bundle.getString(CalendarContract.Events.EVENT_LOCATION, ""); + model.mDescription= bundle.getString(CalendarContract.Events.DESCRIPTION, ""); + model.mUrl= bundle.getString(ExtendedProperty.URL, ""); + model.mOrganizer= bundle.getString(CalendarContract.Events.ORGANIZER, ""); + model.mRrule= bundle.getString(CalendarContract.Events.RRULE, ""); + + model.mStart = bundle.getLong(EXTRA_EVENT_BEGIN_TIME, -1); + model.mEnd = bundle.getLong(EXTRA_EVENT_END_TIME, -1); + model.mAllDay = bundle.getBoolean(EXTRA_EVENT_ALL_DAY, false); + + + return model; + } + + @NonNull public static Bundle createBundleFromEvent(VEvent event, Context context) { Bundle bundle = new Bundle(); @@ -101,7 +122,7 @@ public class EventUtils { } @NonNull - public static EventInfo crateEventInfoFromBundle(long eventId, @Nullable Bundle bundle) { + public static EventInfo createEventInfoFromBundle(long eventId, @Nullable Bundle bundle) { EventInfo eventInfo = new EventInfo(); if (bundle == null) return eventInfo; -- GitLab From ff1e0137cbfbb3cc50ae3ce0793b80b926098813 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 20 Aug 2024 18:03:43 +0200 Subject: [PATCH 17/45] fix: Add missing CalendarID to CalendarEventModel before to try to persist them in DB --- app/src/main/java/com/android/calendar/AllInOneActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 664f7e68c..a9b6f0f21 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -253,7 +253,8 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH final EditEventHelper mEditEventHelper = new EditEventHelper(AllInOneActivity.this); int addedEventcounter = 0; for (CalendarEventModel event : eventList) { - //parse eventInfo to CalendarEventModel + + event.mCalendarId = selectedCalendarId; if( mEditEventHelper.saveEvent(event, null, 0, null)) { addedEventcounter++; -- GitLab From 901d7398a4cc61268e4cfb059867c242a81a8383 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Wed, 21 Aug 2024 13:38:05 +0600 Subject: [PATCH 18/45] fix: make saving event working with mOwner account being set TODO: Check why editing the event is not working. --- .../android/calendar/AllInOneActivity.java | 6 ++-- .../event/CalendarPickerDialogFragment.java | 34 +++++++++++++------ .../calendar/event/EditEventHelper.java | 3 +- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index a9b6f0f21..f9a72127a 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -247,14 +247,16 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH fragment.dismiss(); } - CalendarPickerDialogFragment.CalendarPickerDialogListener listener = selectedCalendarId -> { - Log.d("FAHIM", "Selected calendar id is: "+selectedCalendarId); + CalendarPickerDialogFragment.CalendarPickerDialogListener listener = (selectedCalendarId, ownerAccount, syncAccountName) -> { + Log.d(TAG, "Selected calendar id is: "+selectedCalendarId); final EditEventHelper mEditEventHelper = new EditEventHelper(AllInOneActivity.this); int addedEventcounter = 0; for (CalendarEventModel event : eventList) { event.mCalendarId = selectedCalendarId; + event.mOwnerAccount = ownerAccount; + event.mSyncAccountName = syncAccountName; if( mEditEventHelper.saveEvent(event, null, 0, null)) { addedEventcounter++; diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index f2871da6a..7b946f257 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -37,6 +37,9 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap private static final String TAG = CalendarPickerDialogFragment.class.getName(); public static final String FRAGMENT_TAG = "PICK_CALENDAR_DIALOG"; private static final int TOKEN_CALENDARS = 8; + private static final int EVENT_INDEX_OWNER_ACCOUNT = 2; // TODO: 21/08/2024 Compare with regular event flow + private static final int EVENT_INDEX_ACCOUNT_NAME = 11; // TODO: 21/08/2024 Compare with regular event flow + private CalendarQueryHandler mCalendarQueryHandler; private long mPickedCalendarId = -1; @@ -44,12 +47,15 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap private View mView; private AlertDialog mAlertDialog; private Cursor mCalendarsCursor; + private String mOwnerAccount; + public String mSyncAccountName = null; + private Spinner mCalendarsSpinner; private CalendarPickerDialogListener mListener; //instance of class that request to show this dialog and which expect the selected calendar public CalendarPickerDialogFragment(CalendarPickerDialogListener listener) { - mListener = listener; + mListener = listener; // FIXME: 21/08/2024 Change the way listener is attached to fragment } @NonNull @@ -71,7 +77,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap .setView(mView) .setPositiveButton(R.string.pick_calendar_dialog_validate, (dialog, which) -> { - mListener.onCalendarPicked(mPickedCalendarId); + mListener.onCalendarPicked(mPickedCalendarId, mOwnerAccount, mSyncAccountName); dismiss(); }) .setNegativeButton(android.R.string.cancel, (dialog, which) -> dismiss()) @@ -94,7 +100,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap mCalendarQueryHandler = new CalendarQueryHandler(contentResolver); - Log.d("FAHIM", "onCreateView call()"); + Log.d(TAG, "onCreateView call()"); mCalendarQueryHandler.startQuery(TOKEN_CALENDARS, null, CalendarContract.Calendars.CONTENT_URI, EditEventHelper.CALENDARS_PROJECTION, EditEventHelper.CALENDARS_WHERE_WRITEABLE_VISIBLE, null /* selection args */, @@ -112,6 +118,13 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap return; } + if (cursor.moveToFirst()) { + mOwnerAccount = cursor.getString(EVENT_INDEX_OWNER_ACCOUNT); + + // TODO: 21/08/2024 Check why its needed while editing the event only, not importing + mSyncAccountName = cursor.getString(EVENT_INDEX_ACCOUNT_NAME); + } + // populate the calendars spinner CalPickerAdapter adapter = new CalPickerAdapter(getContext(), R.layout.calendars_spinner_item, mCalendarsCursor); @@ -121,14 +134,15 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { - Log.d("FAHIM", "onItemSelected.id: "+id+", position: "+position); + Log.d(TAG, "onItemSelected.id: "+id+", position: "+position); //todo inspire from EditEventView.onItemSelected line 1503 mPickedCalendarId = id; + } @Override public void onNothingSelected(AdapterView parent) { - Log.d("FAHIM", "onNothingSelected"); + Log.d(TAG, "onNothingSelected"); mPickedCalendarId = -1; } @@ -148,11 +162,11 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap return; } - Log.d("FAHIM", "token is: "+token+", cursor: "+cursor.getCount()); + Log.d(TAG, "token is: "+token+", cursor: "+cursor.getCount()); final Activity activity = CalendarPickerDialogFragment.this.getActivity(); if (activity == null || activity.isFinishing()) { - Log.d("FAHIM", "activity is null or finishing"); + Log.d(TAG, "activity is null or finishing"); cursor.close(); return; } @@ -160,7 +174,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap try { MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); if (DEBUG) { - Log.d("FAHIM", "onQueryComplete: setting cursor with " + Log.d(TAG, "onQueryComplete: setting cursor with " + matrixCursor.getCount() + " calendars"); } CalendarPickerDialogFragment.this.setCalendarsCursor(matrixCursor, true); @@ -172,7 +186,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap } public interface CalendarPickerDialogListener { - public void onCalendarPicked(long selectedCalendarId); + public void onCalendarPicked(long selectedCalendarId, String ownerAccount, String syncAccountName); } public static class CalPickerAdapter extends ResourceCursorAdapter { @@ -184,7 +198,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap @Override public void bindView(View view, Context context, Cursor cursor) { - Log.d("FAHIM", "Call CalendarsAdapter's bindView with cursor: "+cursor.getCount()); +// Log.d(TAG, "Call CalendarsAdapter's bindView with cursor: "+cursor.getCount()); View colorBar = view.findViewById(R.id.color); int colorColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_COLOR); if (colorBar != null) { diff --git a/app/src/main/java/com/android/calendar/event/EditEventHelper.java b/app/src/main/java/com/android/calendar/event/EditEventHelper.java index 2b68bfdff..1c41a320d 100644 --- a/app/src/main/java/com/android/calendar/event/EditEventHelper.java +++ b/app/src/main/java/com/android/calendar/event/EditEventHelper.java @@ -43,6 +43,7 @@ import com.android.calendar.CalendarEventModel; import com.android.calendar.CalendarEventModel.Attendee; import com.android.calendar.CalendarEventModel.ReminderEntry; import com.android.calendar.Utils; +import com.android.calendarcommon2.BuildConfig; import com.android.calendarcommon2.DateException; import com.android.calendarcommon2.EventRecurrence; import com.android.calendarcommon2.RecurrenceProcessor; @@ -60,7 +61,7 @@ import java.util.TimeZone; public class EditEventHelper { private static final String TAG = "EditEventHelper"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; // FIXME: 21/08/2024 Change to false when testing done // Used for parsing rrules for special cases. private EventRecurrence mEventRecurrence = new EventRecurrence(); -- GitLab From b758a93e9a8fb2b60737d036e1631d4febda16d1 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 21 Aug 2024 11:32:28 +0200 Subject: [PATCH 19/45] feature: imported .ics file now also include attendees --- .../android/calendar/AllInOneActivity.java | 7 +- .../android/calendar/CalendarEventModel.java | 89 +++++++++++++++++++ .../java/com/android/calendar/EventUtils.java | 58 ------------ 3 files changed, 94 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index f9a72127a..32728e9da 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -476,10 +476,13 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH } } } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) { - eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID); + eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID, -1); } - final CalendarEventModel eventModel = EventUtils.createEventModelFromBundle(eventId, bundle); + final CalendarEventModel eventModel = new CalendarEventModel(getApplicationContext(), bundle); + if (eventId > -1) { + eventModel.mId = eventId; + } eventModelList.add(eventModel); } diff --git a/app/src/main/java/com/android/calendar/CalendarEventModel.java b/app/src/main/java/com/android/calendar/CalendarEventModel.java index 0d40e260e..bf6092a85 100644 --- a/app/src/main/java/com/android/calendar/CalendarEventModel.java +++ b/app/src/main/java/com/android/calendar/CalendarEventModel.java @@ -16,9 +16,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; @@ -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); diff --git a/app/src/main/java/com/android/calendar/EventUtils.java b/app/src/main/java/com/android/calendar/EventUtils.java index fc11991b6..ca59fc09e 100644 --- a/app/src/main/java/com/android/calendar/EventUtils.java +++ b/app/src/main/java/com/android/calendar/EventUtils.java @@ -37,27 +37,6 @@ public class EventUtils { private EventUtils() { } - - @NonNull - public static CalendarEventModel createEventModelFromBundle(long eventId, @NonNull Bundle bundle) { - CalendarEventModel model = new CalendarEventModel(); - model.mId = eventId; - model.mTitle = bundle.getString(CalendarContract.Events.TITLE, ""); - model.mLocation= bundle.getString(CalendarContract.Events.EVENT_LOCATION, ""); - model.mDescription= bundle.getString(CalendarContract.Events.DESCRIPTION, ""); - model.mUrl= bundle.getString(ExtendedProperty.URL, ""); - model.mOrganizer= bundle.getString(CalendarContract.Events.ORGANIZER, ""); - model.mRrule= bundle.getString(CalendarContract.Events.RRULE, ""); - - model.mStart = bundle.getLong(EXTRA_EVENT_BEGIN_TIME, -1); - model.mEnd = bundle.getLong(EXTRA_EVENT_END_TIME, -1); - model.mAllDay = bundle.getBoolean(EXTRA_EVENT_ALL_DAY, false); - - - return model; - } - - @NonNull public static Bundle createBundleFromEvent(VEvent event, Context context) { Bundle bundle = new Bundle(); @@ -121,43 +100,6 @@ public class EventUtils { return bundle; } - @NonNull - public static EventInfo createEventInfoFromBundle(long eventId, @Nullable Bundle bundle) { - EventInfo eventInfo = new EventInfo(); - - if (bundle == null) return eventInfo; - - boolean allDay = bundle.getBoolean(EXTRA_EVENT_ALL_DAY, false); - - long begin = bundle.getLong(EXTRA_EVENT_BEGIN_TIME, -1); - long end = bundle.getLong(EXTRA_EVENT_END_TIME, -1); - if (end != -1) { - eventInfo.endTime = new Time(); - if (allDay) { - eventInfo.endTime.setTimezone(Time.TIMEZONE_UTC); - } - eventInfo.endTime.set(end); - } - if (begin != -1) { - eventInfo.startTime = new Time(); - if (allDay) { - eventInfo.startTime.setTimezone(Time.TIMEZONE_UTC); - } - eventInfo.startTime.set(begin); - } - eventInfo.id = eventId; - eventInfo.eventTitle = bundle.getString(CalendarContract.Events.TITLE); - eventInfo.calendarId = bundle.getLong(CalendarContract.Events.CALENDAR_ID, -1); - - if (allDay) { - eventInfo.extraLong = CalendarController.EXTRA_CREATE_ALL_DAY; - } else { - eventInfo.extraLong = 0; - } - - return eventInfo; - } - private static boolean isTimeStartOfDay(String dtStart, String dtStartParam, Context context) { // convert to epoch milli seconds long timeStamp = getLocalTimeFromString(dtStart, dtStartParam, context); -- GitLab From 906decb0c2e6139b5975d80a489d4ab990ab28f0 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Wed, 21 Aug 2024 18:10:49 +0600 Subject: [PATCH 20/45] fix: add save and restore flow for configuration changes --- .../android/calendar/AllInOneActivity.java | 87 +++---- .../event/CalendarPickerDialogFragment.java | 217 +++++++++++------- 2 files changed, 176 insertions(+), 128 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 32728e9da..00f572c88 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -99,6 +99,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; @@ -106,14 +107,15 @@ import java.util.TimeZone; import ws.xsoh.etar.R; public class AllInOneActivity extends AbstractCalendarActivity implements EventHandler, - OnSharedPreferenceChangeListener, SearchView.OnQueryTextListener, SearchView.OnSuggestionListener, NavigationView.OnNavigationItemSelectedListener { - public static final String BUNDLE_KEY_MULTIPLE_EVENTS = "key_multiple_events"; + OnSharedPreferenceChangeListener, SearchView.OnQueryTextListener, SearchView.OnSuggestionListener, NavigationView.OnNavigationItemSelectedListener, CalendarPickerDialogFragment.CalendarPickerDialogListener { 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"; + public static final String BUNDLE_KEY_MULTIPLE_EVENTS = "key_multiple_events"; + 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; @@ -237,9 +239,10 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH private LinearLayout.LayoutParams mVerticalControlsParams; private AllInOneMenuExtensionsInterface mExtensions = ExtensionsFactory .getAllInOneMenuExtensions(); + private List mEventList = Collections.emptyList(); - private void showCalendarChooserDialog(List eventList) { - final FragmentManager fragmentManager = getSupportFragmentManager(); + private void showCalendarPickerDialog() { + FragmentManager fragmentManager = getSupportFragmentManager(); CalendarPickerDialogFragment fragment = ((CalendarPickerDialogFragment) fragmentManager.findFragmentByTag(CalendarPickerDialogFragment.FRAGMENT_TAG)); @@ -247,41 +250,10 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH fragment.dismiss(); } - CalendarPickerDialogFragment.CalendarPickerDialogListener listener = (selectedCalendarId, ownerAccount, syncAccountName) -> { - Log.d(TAG, "Selected calendar id is: "+selectedCalendarId); - - final EditEventHelper mEditEventHelper = new EditEventHelper(AllInOneActivity.this); - int addedEventcounter = 0; - for (CalendarEventModel event : eventList) { - - event.mCalendarId = selectedCalendarId; - event.mOwnerAccount = ownerAccount; - event.mSyncAccountName = syncAccountName; - - if( mEditEventHelper.saveEvent(event, null, 0, null)) { - addedEventcounter++; - } else { - // FIXME 20/08/2024 is it the expected behaviour ? check & update - // Replace raw string by value from res folder - Toast.makeText(getApplicationContext(), "Failed to add : "+event.mTitle+ "to the calendar", Toast.LENGTH_SHORT); - } - } - if (addedEventcounter > 0) { - // FIXME Replace raw string by value from res folder - Toast.makeText(getApplicationContext(), addedEventcounter + "events as been added to the calendar", Toast.LENGTH_SHORT); - } - - }; - fragment = new CalendarPickerDialogFragment(listener); + fragment = new CalendarPickerDialogFragment(); fragment.show(fragmentManager, CalendarPickerDialogFragment.FRAGMENT_TAG); - - // FIXME: 19/08/2024 Remove when implementation done - String message = String.format(Locale.getDefault(), "Found %d events from .ics file import", eventList.size()); - Log.i(TAG, message); - Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); } - @Override protected void onNewIntent(Intent intent) { String action = intent.getAction(); @@ -439,20 +411,22 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH mContentResolver = getContentResolver(); - // Ignore handling events on activity recreation on configuration changes + // Save and restore flow for handling import of multiple events if (icicle == null) { handleEvents(icicle, getIntent()); + } else { + mEventList = ((List) icicle.getSerializable(BUNDLE_KEY_RESTORE_MULTIPLE_EVENTS)); } } private void handleEvents(Bundle bundle, Intent intent) { - final List eventList = getEventList(bundle, intent); - if (eventList.isEmpty()) { + 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. - showCalendarChooserDialog(eventList); + showCalendarPickerDialog(); } private List getEventList(Bundle icicle, Intent intent) { @@ -803,6 +777,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) { @@ -1586,6 +1561,38 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH return false; } + @Override + public void onCalendarPicked(long selectedCalendarId, String ownerAccount, String syncAccountName) { + Log.d(TAG, "onCalendarPicked: selectedCalendarId = " + selectedCalendarId); + saveEventsInPickedCalendar(selectedCalendarId, ownerAccount, syncAccountName); + } + + 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 { + // FIXME 20/08/2024 is it the expected behaviour ? check & update + // Replace raw string by value from res folder + Toast.makeText(this, "Failed to add " + event.mTitle + " to the calendar", + Toast.LENGTH_SHORT).show(); + } + } + if (addedEventcounter > 0) { + // FIXME Replace raw string by value from res folder + Toast.makeText(this, String.format(Locale.getDefault(), + "Added %d events to the calendar", addedEventcounter), Toast.LENGTH_SHORT).show(); + } + } + private class QueryHandler extends AsyncQueryHandler { public QueryHandler(ContentResolver cr) { super(cr); diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index 7b946f257..2b88d2e96 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -16,6 +16,7 @@ import android.provider.CalendarContract; import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ResourceCursorAdapter; import android.widget.Spinner; @@ -23,6 +24,7 @@ import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; import com.android.calendar.Utils; @@ -37,79 +39,111 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap private static final String TAG = CalendarPickerDialogFragment.class.getName(); public static final String FRAGMENT_TAG = "PICK_CALENDAR_DIALOG"; private static final int TOKEN_CALENDARS = 8; + public static final int SPINNER_DEFAULT_POSITION = 0; private static final int EVENT_INDEX_OWNER_ACCOUNT = 2; // TODO: 21/08/2024 Compare with regular event flow private static final int EVENT_INDEX_ACCOUNT_NAME = 11; // TODO: 21/08/2024 Compare with regular event flow - - private CalendarQueryHandler mCalendarQueryHandler; + private static final String BUNDLE_KEY_RESTORE_SELECTED_SPINNER_ITEM_POSITION = "selected_spinner_item_position"; private long mPickedCalendarId = -1; - - private View mView; - private AlertDialog mAlertDialog; private Cursor mCalendarsCursor; private String mOwnerAccount; public String mSyncAccountName = null; private Spinner mCalendarsSpinner; + private int mSelectedSpinnerItemPosition = SPINNER_DEFAULT_POSITION; - private CalendarPickerDialogListener mListener; //instance of class that request to show this dialog and which expect the selected calendar + // Instance of class that request to show this dialog and which expect the selected calendar + private CalendarPickerDialogListener calendarPickerDialogListener; - public CalendarPickerDialogFragment(CalendarPickerDialogListener listener) { - mListener = listener; // FIXME: 21/08/2024 Change the way listener is attached to fragment + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + if (context instanceof CalendarPickerDialogListener) { + calendarPickerDialogListener = (CalendarPickerDialogListener) context; + } else { + throw new RuntimeException(context + " must implement CalendarPickerDialogListener"); + } + } + + @Override + public void onDetach() { + calendarPickerDialogListener = null; + super.onDetach(); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + Log.d(TAG, "onCreate: bundle = " + savedInstanceState); + super.onCreate(savedInstanceState); + + initiateCalendarQueryHandler(); + + // Save and restore flow + if (savedInstanceState != null) { + mSelectedSpinnerItemPosition = savedInstanceState.getInt(BUNDLE_KEY_RESTORE_SELECTED_SPINNER_ITEM_POSITION); + Log.d(TAG, "onCreate: restored spinner position to " + mSelectedSpinnerItemPosition); + } + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(BUNDLE_KEY_RESTORE_SELECTED_SPINNER_ITEM_POSITION, mSelectedSpinnerItemPosition); + Log.d(TAG, "onSaveInstanceState: saved spinner position to " + mSelectedSpinnerItemPosition); + } + + @Override + public void onDestroyView() { + Log.d(TAG, "onDestroyView: "); + closeCursor(); + super.onDestroyView(); } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - final Activity activity = getActivity(); - //TODO check activity is not null + Log.d(TAG, "onCreateDialog: "); - final LayoutInflater layoutInflater = (LayoutInflater) activity - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + // onCreateDialog() gets called before onCreateView(), so the view needs to be created and supplied here + // getView() returns null at this point + View view = LayoutInflater.from(requireContext()).inflate(R.layout.pick_calendar_dialog, null); - initiateCalendarQueryHandler(getActivity()); + mCalendarsSpinner = view.findViewById(R.id.calendars_spinner); + mCalendarsSpinner.setSelection(mSelectedSpinnerItemPosition); - mView = layoutInflater.inflate(R.layout.pick_calendar_dialog, null); - mCalendarsSpinner = (Spinner) mView.findViewById(R.id.calendars_spinner); - - mAlertDialog = new AlertDialog.Builder(activity) + return new AlertDialog.Builder(requireActivity()) .setTitle(R.string.pick_calendar_dialog_title) - .setView(mView) + .setView(view) .setPositiveButton(R.string.pick_calendar_dialog_validate, (dialog, which) -> { - mListener.onCalendarPicked(mPickedCalendarId, mOwnerAccount, mSyncAccountName); + calendarPickerDialogListener.onCalendarPicked(mPickedCalendarId, mOwnerAccount, mSyncAccountName); dismiss(); }) .setNegativeButton(android.R.string.cancel, (dialog, which) -> dismiss()) - .setOnDismissListener(dialog -> { - if (mCalendarsCursor != null && !mCalendarsCursor.isClosed()) { - mCalendarsCursor.close(); - } - }) .create(); - - return mAlertDialog; } + private void closeCursor() { + if (mCalendarsCursor != null && !mCalendarsCursor.isClosed()) { + Log.d(TAG, "closing cursor"); + mCalendarsCursor.close(); + } + } - private void initiateCalendarQueryHandler(@NonNull Activity activity) { - final ContentResolver contentResolver = activity.getContentResolver(); + private void initiateCalendarQueryHandler() { + final ContentResolver contentResolver = requireActivity().getContentResolver(); if (contentResolver == null) { - return ; // TODO handle properly this case + return; } - mCalendarQueryHandler = new CalendarQueryHandler(contentResolver); - - Log.d(TAG, "onCreateView call()"); - mCalendarQueryHandler.startQuery(TOKEN_CALENDARS, null, CalendarContract.Calendars.CONTENT_URI, + CalendarQueryHandler calendarQueryHandler = new CalendarQueryHandler(contentResolver); + calendarQueryHandler.startQuery(TOKEN_CALENDARS, null, CalendarContract.Calendars.CONTENT_URI, EditEventHelper.CALENDARS_PROJECTION, EditEventHelper.CALENDARS_WHERE_WRITEABLE_VISIBLE, null /* selection args */, null /* sort order */); - } public void setCalendarsCursor(Cursor cursor, boolean userVisible) { - mCalendarsCursor = cursor; if (cursor == null || cursor.getCount() == 0) { if (!userVisible) { return; @@ -118,26 +152,37 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap return; } - if (cursor.moveToFirst()) { - mOwnerAccount = cursor.getString(EVENT_INDEX_OWNER_ACCOUNT); + mCalendarsCursor = cursor; - // TODO: 21/08/2024 Check why its needed while editing the event only, not importing - mSyncAccountName = cursor.getString(EVENT_INDEX_ACCOUNT_NAME); + if (mCalendarsCursor.moveToFirst()) { + mOwnerAccount = mCalendarsCursor.getString(EVENT_INDEX_OWNER_ACCOUNT); + // TODO: 21/08/2024 Check why its needed while editing the event only, not importing + mSyncAccountName = mCalendarsCursor.getString(EVENT_INDEX_ACCOUNT_NAME); + bindCalendarSpinnerUi(); + } else { + Log.i(TAG, "Cursor doesn't have any calendar data."); } + } - // populate the calendars spinner - CalPickerAdapter adapter = new CalPickerAdapter(getContext(), R.layout.calendars_spinner_item, mCalendarsCursor); + private void bindCalendarSpinnerUi() { + CalendarPickerAdapter adapter = new CalendarPickerAdapter(requireContext(), + R.layout.calendars_spinner_item, + mCalendarsCursor); mCalendarsSpinner.setAdapter(adapter); mCalendarsSpinner.setOnItemSelectedListener(this); + + // Restore Spinner's position if there's any saved on configuration changes + if (mSelectedSpinnerItemPosition != SPINNER_DEFAULT_POSITION) { + mCalendarsSpinner.setSelection(mSelectedSpinnerItemPosition); + } } @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { - Log.d(TAG, "onItemSelected.id: "+id+", position: "+position); - //todo inspire from EditEventView.onItemSelected line 1503 + Log.d(TAG, "onItemSelected: id: " + id + ", position: " + position); + mSelectedSpinnerItemPosition = position; mPickedCalendarId = id; - } @Override @@ -147,58 +192,19 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap } - public class CalendarQueryHandler extends AsyncQueryHandler { - - public CalendarQueryHandler(ContentResolver cr) { - super(cr); - } - - @Override - protected void onQueryComplete(int token, Object cookie, Cursor cursor) { - super.onQueryComplete(token, cookie, cursor); - - // If the query didn't return a cursor for some reason return - if (cursor == null) { - return; - } - - Log.d(TAG, "token is: "+token+", cursor: "+cursor.getCount()); - - final Activity activity = CalendarPickerDialogFragment.this.getActivity(); - if (activity == null || activity.isFinishing()) { - Log.d(TAG, "activity is null or finishing"); - cursor.close(); - return; - } - - try { - MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); - if (DEBUG) { - Log.d(TAG, "onQueryComplete: setting cursor with " - + matrixCursor.getCount() + " calendars"); - } - CalendarPickerDialogFragment.this.setCalendarsCursor(matrixCursor, true); - - } finally { - cursor.close(); - } - } - } - public interface CalendarPickerDialogListener { - public void onCalendarPicked(long selectedCalendarId, String ownerAccount, String syncAccountName); + void onCalendarPicked(long selectedCalendarId, String ownerAccount, String syncAccountName); } - public static class CalPickerAdapter extends ResourceCursorAdapter { + public static class CalendarPickerAdapter extends ResourceCursorAdapter { - public CalPickerAdapter(Context context, int resourceId, Cursor cursor) { + public CalendarPickerAdapter(Context context, int resourceId, Cursor cursor) { super(context, resourceId, cursor, 0); //No selection flags added to avoid deprecated call setDropDownViewResource(R.layout.calendars_dropdown_item); } @Override public void bindView(View view, Context context, Cursor cursor) { -// Log.d(TAG, "Call CalendarsAdapter's bindView with cursor: "+cursor.getCount()); View colorBar = view.findViewById(R.id.color); int colorColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_COLOR); if (colorBar != null) { @@ -209,12 +215,12 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap int nameColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME); int ownerColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.OWNER_ACCOUNT); - TextView name = (TextView) view.findViewById(R.id.calendar_name); + TextView name = view.findViewById(R.id.calendar_name); if (name != null) { String displayName = cursor.getString(nameColumn); name.setText(displayName); - TextView accountName = (TextView) view.findViewById(R.id.account_name); + TextView accountName = view.findViewById(R.id.account_name); if (accountName != null) { accountName.setText(cursor.getString(ownerColumn)); accountName.setVisibility(TextView.VISIBLE); @@ -222,5 +228,40 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap } } } + + // FIXME: 21/08/2024 Make it static + public class CalendarQueryHandler extends AsyncQueryHandler { + + public CalendarQueryHandler(ContentResolver cr) { + super(cr); + } + + @Override + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + super.onQueryComplete(token, cookie, cursor); + + // If the query didn't return a cursor for some reason return + if (cursor == null) { + return; + } + + final Activity activity = CalendarPickerDialogFragment.this.getActivity(); + if (activity == null || activity.isFinishing()) { + Log.d(TAG, "activity is null or finishing"); + cursor.close(); + return; + } + + try (cursor) { + MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); + if (DEBUG) { + Log.d(TAG, "onQueryComplete: setting cursor with " + + matrixCursor.getCount() + " calendars"); + } + // FIXME: 21/08/2024 Change to lambda for making this class static + CalendarPickerDialogFragment.this.setCalendarsCursor(matrixCursor, true); + } + } + } } -- GitLab From 3b50b8ef715acbb240d22a93080ff63bc7932fac Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Wed, 21 Aug 2024 19:27:54 +0600 Subject: [PATCH 21/45] fix: optimize code for memory leak prevention Also, extracted logic into separate classes. --- .../android/calendar/AllInOneActivity.java | 11 ++ .../calendar/event/CalendarPickerAdapter.java | 44 ++++++++ .../event/CalendarPickerDialogFragment.java | 106 ++++-------------- .../event/CalendarPickerQueryHandler.java | 53 +++++++++ .../event/CalendarPickerQueryListener.java | 11 ++ 5 files changed, 138 insertions(+), 87 deletions(-) create mode 100644 app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java create mode 100644 app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java create mode 100644 app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 00f572c88..823a9f645 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -1567,6 +1567,17 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH saveEventsInPickedCalendar(selectedCalendarId, ownerAccount, syncAccountName); } + @Override + public void onCalendarUnavailable() { + Toast.makeText(this, R.string.no_calendars_found, Toast.LENGTH_SHORT).show(); + } + + @Override + public void onCalendarPickerError() { + // TODO: 21/08/2024 Use localized strings + Toast.makeText(this, "Couldn't add events to the calendar", Toast.LENGTH_SHORT).show(); + } + private void saveEventsInPickedCalendar(long selectedCalendarId, String ownerAccount, String syncAccountName) { final EditEventHelper editEventHelper = new EditEventHelper(this); diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java b/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java new file mode 100644 index 000000000..cb90832cf --- /dev/null +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java @@ -0,0 +1,44 @@ +package com.android.calendar.event; + +import android.content.Context; +import android.database.Cursor; +import android.provider.CalendarContract; +import android.view.View; +import android.widget.ResourceCursorAdapter; +import android.widget.TextView; + +import com.android.calendar.Utils; + +import ws.xsoh.etar.R; + +public class CalendarPickerAdapter extends ResourceCursorAdapter { + + public CalendarPickerAdapter(Context context, int resourceId, Cursor cursor) { + super(context, resourceId, cursor, 0); // No selection flags added to avoid deprecated call + setDropDownViewResource(R.layout.calendars_dropdown_item); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + View colorBar = view.findViewById(R.id.color); + int colorColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_COLOR); + if (colorBar != null) { + colorBar.setBackgroundColor(Utils.getDisplayColorFromColor(context, cursor.getInt(colorColumn))); + } + + int nameColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME); + int ownerColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.OWNER_ACCOUNT); + + TextView name = view.findViewById(R.id.calendar_name); + if (name != null) { + String displayName = cursor.getString(nameColumn); + name.setText(displayName); + + TextView accountName = view.findViewById(R.id.account_name); + if (accountName != null) { + accountName.setText(cursor.getString(ownerColumn)); + accountName.setVisibility(TextView.VISIBLE); + } + } + } +} diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index 2b88d2e96..1ec59d04d 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -1,40 +1,29 @@ package com.android.calendar.event; -import static ws.xsoh.etar.BuildConfig.DEBUG; - -import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; -import android.content.AsyncQueryHandler; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; -import android.database.MatrixCursor; import android.os.Bundle; import android.provider.CalendarContract; import android.util.Log; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.ResourceCursorAdapter; import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; -import com.android.calendar.Utils; - import ws.xsoh.etar.R; /** * Allows the user to quickly import a multi event cal file. */ -public class CalendarPickerDialogFragment extends DialogFragment implements AdapterView.OnItemSelectedListener { +public class CalendarPickerDialogFragment extends DialogFragment implements AdapterView.OnItemSelectedListener, CalendarPickerQueryListener { private static final String TAG = CalendarPickerDialogFragment.class.getName(); public static final String FRAGMENT_TAG = "PICK_CALENDAR_DIALOG"; @@ -136,22 +125,15 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap return; } - CalendarQueryHandler calendarQueryHandler = new CalendarQueryHandler(contentResolver); - calendarQueryHandler.startQuery(TOKEN_CALENDARS, null, CalendarContract.Calendars.CONTENT_URI, + CalendarPickerQueryHandler calendarPickerQueryHandler = new CalendarPickerQueryHandler(contentResolver, requireActivity(), this); + + calendarPickerQueryHandler.startQuery(TOKEN_CALENDARS, null, CalendarContract.Calendars.CONTENT_URI, EditEventHelper.CALENDARS_PROJECTION, EditEventHelper.CALENDARS_WHERE_WRITEABLE_VISIBLE, null /* selection args */, null /* sort order */); } - public void setCalendarsCursor(Cursor cursor, boolean userVisible) { - if (cursor == null || cursor.getCount() == 0) { - if (!userVisible) { - return; - } - Toast.makeText(getContext(), R.string.no_calendars_found, Toast.LENGTH_SHORT).show(); - return; - } - + public void setCalendarsCursor(Cursor cursor) { mCalendarsCursor = cursor; if (mCalendarsCursor.moveToFirst()) { @@ -191,77 +173,27 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap mPickedCalendarId = -1; } - - public interface CalendarPickerDialogListener { - void onCalendarPicked(long selectedCalendarId, String ownerAccount, String syncAccountName); + @Override + public void onCursorAvailable(Cursor cursor, boolean userVisible) { + setCalendarsCursor(cursor); } - public static class CalendarPickerAdapter extends ResourceCursorAdapter { - - public CalendarPickerAdapter(Context context, int resourceId, Cursor cursor) { - super(context, resourceId, cursor, 0); //No selection flags added to avoid deprecated call - setDropDownViewResource(R.layout.calendars_dropdown_item); - } + @Override + public void onCursorUnavailable() { + calendarPickerDialogListener.onCalendarUnavailable(); + } - @Override - public void bindView(View view, Context context, Cursor cursor) { - View colorBar = view.findViewById(R.id.color); - int colorColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_COLOR); - if (colorBar != null) { - colorBar.setBackgroundColor(Utils.getDisplayColorFromColor(context, - cursor.getInt(colorColumn))); - } - - int nameColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME); - int ownerColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.OWNER_ACCOUNT); - - TextView name = view.findViewById(R.id.calendar_name); - if (name != null) { - String displayName = cursor.getString(nameColumn); - name.setText(displayName); - - TextView accountName = view.findViewById(R.id.account_name); - if (accountName != null) { - accountName.setText(cursor.getString(ownerColumn)); - accountName.setVisibility(TextView.VISIBLE); - } - } - } + @Override + public void onCalendarPickerError() { + calendarPickerDialogListener.onCalendarPickerError(); } - // FIXME: 21/08/2024 Make it static - public class CalendarQueryHandler extends AsyncQueryHandler { + public interface CalendarPickerDialogListener { + void onCalendarPicked(long selectedCalendarId, String ownerAccount, String syncAccountName); - public CalendarQueryHandler(ContentResolver cr) { - super(cr); - } + void onCalendarUnavailable(); - @Override - protected void onQueryComplete(int token, Object cookie, Cursor cursor) { - super.onQueryComplete(token, cookie, cursor); - - // If the query didn't return a cursor for some reason return - if (cursor == null) { - return; - } - - final Activity activity = CalendarPickerDialogFragment.this.getActivity(); - if (activity == null || activity.isFinishing()) { - Log.d(TAG, "activity is null or finishing"); - cursor.close(); - return; - } - - try (cursor) { - MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); - if (DEBUG) { - Log.d(TAG, "onQueryComplete: setting cursor with " - + matrixCursor.getCount() + " calendars"); - } - // FIXME: 21/08/2024 Change to lambda for making this class static - CalendarPickerDialogFragment.this.setCalendarsCursor(matrixCursor, true); - } - } + void onCalendarPickerError(); } } diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java new file mode 100644 index 000000000..2571ceaae --- /dev/null +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java @@ -0,0 +1,53 @@ +package com.android.calendar.event; + +import static ws.xsoh.etar.BuildConfig.DEBUG; + +import android.app.Activity; +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.util.Log; + +import com.android.calendar.Utils; + +import java.lang.ref.WeakReference; + +public class CalendarPickerQueryHandler extends AsyncQueryHandler { + private static final String TAG = CalendarPickerQueryHandler.class.getSimpleName(); + private final WeakReference activityReference; + private final WeakReference listenerReference; + + public CalendarPickerQueryHandler(ContentResolver contentResolver, Activity activity, CalendarPickerQueryListener calendarPickerQueryListener) { + super(contentResolver); + activityReference = new WeakReference<>(activity); + listenerReference = new WeakReference<>(calendarPickerQueryListener); + } + + @Override + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + super.onQueryComplete(token, cookie, cursor); + + CalendarPickerQueryListener queryListener = listenerReference.get(); + + if (cursor == null || cursor.getCount() == 0) { + queryListener.onCursorUnavailable(); + return; + } + + final Activity activity = activityReference.get(); + if (activity == null || activity.isFinishing()) { + cursor.close(); + queryListener.onCalendarPickerError(); + return; + } + + try (cursor) { + MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); + if (DEBUG) { + Log.d(TAG, "onQueryComplete: found cursor with " + matrixCursor.getCount() + " calendars"); + } + queryListener.onCursorAvailable(matrixCursor, true); + } + } +} diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java new file mode 100644 index 000000000..325431990 --- /dev/null +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java @@ -0,0 +1,11 @@ +package com.android.calendar.event; + +import android.database.Cursor; + +public interface CalendarPickerQueryListener { + void onCursorAvailable(Cursor cursor, boolean userVisible); + + void onCursorUnavailable(); + + void onCalendarPickerError(); +} -- GitLab From 3b0b297875d969c398780766d615029d98293f51 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 21 Aug 2024 16:00:03 +0200 Subject: [PATCH 22/45] fix: close the dialog if no calendar is available --- app/src/main/java/com/android/calendar/AllInOneActivity.java | 3 ++- .../android/calendar/event/CalendarPickerDialogFragment.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 823a9f645..3df94811a 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -1568,7 +1568,8 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH } @Override - public void onCalendarUnavailable() { + public void onCalendarUnavailable(CalendarPickerDialogFragment dialogFragment) { + dialogFragment.dismiss(); Toast.makeText(this, R.string.no_calendars_found, Toast.LENGTH_SHORT).show(); } diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index 1ec59d04d..4434e51a0 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -180,7 +180,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap @Override public void onCursorUnavailable() { - calendarPickerDialogListener.onCalendarUnavailable(); + calendarPickerDialogListener.onCalendarUnavailable(this); } @Override @@ -191,7 +191,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap public interface CalendarPickerDialogListener { void onCalendarPicked(long selectedCalendarId, String ownerAccount, String syncAccountName); - void onCalendarUnavailable(); + void onCalendarUnavailable(CalendarPickerDialogFragment dialogFragment); void onCalendarPickerError(); } -- GitLab From 08f07b518b160209c96e7d2f55748f55ee00da40 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 21 Aug 2024 16:12:57 +0200 Subject: [PATCH 23/45] Task: Apply Rabbit suggestion on ImportActivity.java: add a check that event list is not empty --- app/src/main/java/com/android/calendar/ImportActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/android/calendar/ImportActivity.java b/app/src/main/java/com/android/calendar/ImportActivity.java index 33122d7dc..5a8712f41 100644 --- a/app/src/main/java/com/android/calendar/ImportActivity.java +++ b/app/src/main/java/com/android/calendar/ImportActivity.java @@ -145,7 +145,7 @@ public class ImportActivity extends Activity { } LinkedList events = calendar.getAllEvents(); - if (events == null) { + if (events == null || events.isEmpty()) { showErrorToast(); return; } -- GitLab From a624a74f435a590b5afddd91d8a56f884334d448 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 21 Aug 2024 16:22:06 +0200 Subject: [PATCH 24/45] task: apply Rabbit suggestion about loging exception from catch blocks --- .../java/com/android/calendar/EventUtils.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/android/calendar/EventUtils.java b/app/src/main/java/com/android/calendar/EventUtils.java index ca59fc09e..ba8225c62 100644 --- a/app/src/main/java/com/android/calendar/EventUtils.java +++ b/app/src/main/java/com/android/calendar/EventUtils.java @@ -10,6 +10,7 @@ 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; @@ -33,7 +34,7 @@ import java.util.TimeZone; import ws.xsoh.etar.R; public class EventUtils { - + private final static String TAG = EventUtils.class.getName(); private EventUtils() { } @@ -125,7 +126,8 @@ public class EventUtils { format.parse(iCalDate); format.setTimeZone(TimeZone.getDefault()); return format.getCalendar().getTimeInMillis(); - } catch (ParseException e) { + } catch (ParseException exception) { + Log.e(TAG, "Can't parse iCalDate:", exception); } } @@ -162,7 +164,8 @@ public class EventUtils { try { format.parse(iCalDate); return format.getCalendar().getTimeInMillis(); - } catch (ParseException e) { + } catch (ParseException exception) { + Log.e(TAG, "Can't parse iCalDate:", exception); } } @@ -174,7 +177,8 @@ public class EventUtils { try { format.parse(iCalDate); return format.getCalendar().getTimeInMillis(); - } catch (ParseException e) { + } catch (ParseException exception) { + Log.e(TAG, "Can't parse iCalDate:", exception); } } @@ -186,7 +190,8 @@ public class EventUtils { try { format.parse(iCalDate); return format.getCalendar().getTimeInMillis(); - } catch (ParseException e) { + } catch (ParseException exception) { + Log.e(TAG, "Can't parse iCalDate:", exception); } } -- GitLab From 113f49f2fdf0a4e2d1697877878444819d44da06 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Wed, 21 Aug 2024 20:47:14 +0600 Subject: [PATCH 25/45] fix: improve dialog UI and text --- .../android/calendar/AllInOneActivity.java | 2 +- .../main/res/layout/pick_calendar_dialog.xml | 97 ++----------------- app/src/main/res/values/strings.xml | 5 +- 3 files changed, 9 insertions(+), 95 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 823a9f645..3caee1819 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -1600,7 +1600,7 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH if (addedEventcounter > 0) { // FIXME Replace raw string by value from res folder Toast.makeText(this, String.format(Locale.getDefault(), - "Added %d events to the calendar", addedEventcounter), Toast.LENGTH_SHORT).show(); + "Added %d events to calendar", addedEventcounter), Toast.LENGTH_SHORT).show(); } } diff --git a/app/src/main/res/layout/pick_calendar_dialog.xml b/app/src/main/res/layout/pick_calendar_dialog.xml index 7b431088d..13f32c952 100644 --- a/app/src/main/res/layout/pick_calendar_dialog.xml +++ b/app/src/main/res/layout/pick_calendar_dialog.xml @@ -1,103 +1,18 @@ - - - - - - - - + android:layout_width="match_parent" + android:layout_height="match_parent"> - - - - - - - - - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2dec60ebc..a1b441dfd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -201,11 +201,10 @@ - Ok + SET - Import events + Set calendar for events - In which calendar do you want to import these x events? The events have been created successfully. -- GitLab From f200a6ca6fe399a7bc7a66f21759ab52bebaefae Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Wed, 21 Aug 2024 21:23:33 +0600 Subject: [PATCH 26/45] fix: show dialog to add calendar if there is not any --- .../android/calendar/AllInOneActivity.java | 20 +++++++++++++++---- .../event/CalendarPickerDialogFragment.java | 18 ++++++++--------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 6c9794ee9..a7dce55bb 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -27,6 +27,7 @@ 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; @@ -108,13 +109,13 @@ import ws.xsoh.etar.R; public class AllInOneActivity extends AbstractCalendarActivity implements EventHandler, 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"; - public static final String BUNDLE_KEY_MULTIPLE_EVENTS = "key_multiple_events"; 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; @@ -1568,9 +1569,20 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH } @Override - public void onCalendarUnavailable(CalendarPickerDialogFragment dialogFragment) { - dialogFragment.dismiss(); - Toast.makeText(this, R.string.no_calendars_found, Toast.LENGTH_SHORT).show(); + 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 diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index 4434e51a0..58aaf0225 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -25,19 +25,17 @@ import ws.xsoh.etar.R; */ public class CalendarPickerDialogFragment extends DialogFragment implements AdapterView.OnItemSelectedListener, CalendarPickerQueryListener { - private static final String TAG = CalendarPickerDialogFragment.class.getName(); public static final String FRAGMENT_TAG = "PICK_CALENDAR_DIALOG"; - private static final int TOKEN_CALENDARS = 8; public static final int SPINNER_DEFAULT_POSITION = 0; - private static final int EVENT_INDEX_OWNER_ACCOUNT = 2; // TODO: 21/08/2024 Compare with regular event flow - private static final int EVENT_INDEX_ACCOUNT_NAME = 11; // TODO: 21/08/2024 Compare with regular event flow + private static final String TAG = CalendarPickerDialogFragment.class.getName(); + private static final int TOKEN_CALENDARS = 8; + private static final int EVENT_INDEX_OWNER_ACCOUNT = 2; + private static final int EVENT_INDEX_ACCOUNT_NAME = 11; private static final String BUNDLE_KEY_RESTORE_SELECTED_SPINNER_ITEM_POSITION = "selected_spinner_item_position"; - + private String mSyncAccountName = null; private long mPickedCalendarId = -1; private Cursor mCalendarsCursor; private String mOwnerAccount; - public String mSyncAccountName = null; - private Spinner mCalendarsSpinner; private int mSelectedSpinnerItemPosition = SPINNER_DEFAULT_POSITION; @@ -138,7 +136,6 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap if (mCalendarsCursor.moveToFirst()) { mOwnerAccount = mCalendarsCursor.getString(EVENT_INDEX_OWNER_ACCOUNT); - // TODO: 21/08/2024 Check why its needed while editing the event only, not importing mSyncAccountName = mCalendarsCursor.getString(EVENT_INDEX_ACCOUNT_NAME); bindCalendarSpinnerUi(); } else { @@ -180,7 +177,8 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap @Override public void onCursorUnavailable() { - calendarPickerDialogListener.onCalendarUnavailable(this); + dismiss(); + calendarPickerDialogListener.onCalendarUnavailable(); } @Override @@ -191,7 +189,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap public interface CalendarPickerDialogListener { void onCalendarPicked(long selectedCalendarId, String ownerAccount, String syncAccountName); - void onCalendarUnavailable(CalendarPickerDialogFragment dialogFragment); + void onCalendarUnavailable(); void onCalendarPickerError(); } -- GitLab From 6059aeb67b63a7cd4cc8c53e9370ee4db031fcf5 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Wed, 21 Aug 2024 22:10:49 +0600 Subject: [PATCH 27/45] fix: update dialog with number of events imported --- .../com/android/calendar/AllInOneActivity.java | 7 ++----- .../event/CalendarPickerDialogFragment.java | 16 +++++++++++++++- app/src/main/res/layout/pick_calendar_dialog.xml | 16 +++++++++++++++- app/src/main/res/values/strings.xml | 5 +++-- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index a7dce55bb..0ff58774a 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -251,7 +251,7 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH fragment.dismiss(); } - fragment = new CalendarPickerDialogFragment(); + fragment = new CalendarPickerDialogFragment(mEventList.size()); fragment.show(fragmentManager, CalendarPickerDialogFragment.FRAGMENT_TAG); } @@ -737,7 +737,6 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH mCalIntentReceiver = Utils.setTimeChangesReceiver(this, mTimeChangesUpdater); } - @Override protected void onPause() { super.onPause(); @@ -1611,9 +1610,7 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH } } if (addedEventcounter > 0) { - // FIXME Replace raw string by value from res folder - Toast.makeText(this, String.format(Locale.getDefault(), - "Added %d events to calendar", addedEventcounter), Toast.LENGTH_SHORT).show(); + Toast.makeText(this, getString(R.string.import_multi_event_confirmation_toast), Toast.LENGTH_SHORT).show(); } } diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index 58aaf0225..622891363 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -13,6 +13,7 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.AdapterView; import android.widget.Spinner; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -32,15 +33,23 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap private static final int EVENT_INDEX_OWNER_ACCOUNT = 2; private static final int EVENT_INDEX_ACCOUNT_NAME = 11; private static final String BUNDLE_KEY_RESTORE_SELECTED_SPINNER_ITEM_POSITION = "selected_spinner_item_position"; + private static final String BUNDLE_KEY_RESTORE_NUMBER_OF_EVENTS = "number_of_events"; private String mSyncAccountName = null; private long mPickedCalendarId = -1; private Cursor mCalendarsCursor; private String mOwnerAccount; private Spinner mCalendarsSpinner; private int mSelectedSpinnerItemPosition = SPINNER_DEFAULT_POSITION; - // Instance of class that request to show this dialog and which expect the selected calendar private CalendarPickerDialogListener calendarPickerDialogListener; + private int mNumberOfEvents; + + public CalendarPickerDialogFragment() { + } + + public CalendarPickerDialogFragment(int numberOfEvents) { + this.mNumberOfEvents = numberOfEvents; + } @Override public void onAttach(@NonNull Context context) { @@ -68,6 +77,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap // Save and restore flow if (savedInstanceState != null) { mSelectedSpinnerItemPosition = savedInstanceState.getInt(BUNDLE_KEY_RESTORE_SELECTED_SPINNER_ITEM_POSITION); + mNumberOfEvents = savedInstanceState.getInt(BUNDLE_KEY_RESTORE_NUMBER_OF_EVENTS); Log.d(TAG, "onCreate: restored spinner position to " + mSelectedSpinnerItemPosition); } } @@ -76,6 +86,7 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(BUNDLE_KEY_RESTORE_SELECTED_SPINNER_ITEM_POSITION, mSelectedSpinnerItemPosition); + outState.putInt(BUNDLE_KEY_RESTORE_NUMBER_OF_EVENTS, mNumberOfEvents); Log.d(TAG, "onSaveInstanceState: saved spinner position to " + mSelectedSpinnerItemPosition); } @@ -98,6 +109,9 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap mCalendarsSpinner = view.findViewById(R.id.calendars_spinner); mCalendarsSpinner.setSelection(mSelectedSpinnerItemPosition); + TextView dialogMessage = view.findViewById(R.id.pick_calendar_dialog_text); + dialogMessage.setText(getString(R.string.pick_calendar_dialog_text, mNumberOfEvents)); + return new AlertDialog.Builder(requireActivity()) .setTitle(R.string.pick_calendar_dialog_title) .setView(view) diff --git a/app/src/main/res/layout/pick_calendar_dialog.xml b/app/src/main/res/layout/pick_calendar_dialog.xml index 13f32c952..ef04207f8 100644 --- a/app/src/main/res/layout/pick_calendar_dialog.xml +++ b/app/src/main/res/layout/pick_calendar_dialog.xml @@ -1,9 +1,23 @@ + + + + app:layout_constraintTop_toBottomOf="@id/pick_calendar_dialog_text" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a1b441dfd..990246595 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -201,10 +201,11 @@ - SET + OK - Set calendar for events + Set calendar + In which calendar do you want to import these %d events? The events have been created successfully. -- GitLab From d61e14d43502e82681bfda5629399a6a67a4e3df Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Wed, 21 Aug 2024 22:58:47 +0600 Subject: [PATCH 28/45] refactor: cleanup code --- .../java/com/android/calendar/EventUtils.java | 7 ------- .../calendar/event/EditEventFragment.java | 1 - .../calendar/event/EditEventHelper.java | 3 +-- .../android/calendar/event/EditEventView.java | 2 -- .../calendar/icalendar/IcalendarUtils.java | 19 ------------------- app/src/main/res/layout/import_activity.xml | 6 ------ 6 files changed, 1 insertion(+), 37 deletions(-) delete mode 100644 app/src/main/res/layout/import_activity.xml diff --git a/app/src/main/java/com/android/calendar/EventUtils.java b/app/src/main/java/com/android/calendar/EventUtils.java index ba8225c62..c66e4acee 100644 --- a/app/src/main/java/com/android/calendar/EventUtils.java +++ b/app/src/main/java/com/android/calendar/EventUtils.java @@ -1,9 +1,5 @@ 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.os.Build; @@ -14,15 +10,12 @@ import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.android.calendar.CalendarController.EventInfo; 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 com.android.calendarcommon2.Time; import java.text.DateFormat; import java.text.ParseException; diff --git a/app/src/main/java/com/android/calendar/event/EditEventFragment.java b/app/src/main/java/com/android/calendar/event/EditEventFragment.java index 979af7fd3..8f00a76e0 100644 --- a/app/src/main/java/com/android/calendar/event/EditEventFragment.java +++ b/app/src/main/java/com/android/calendar/event/EditEventFragment.java @@ -758,7 +758,6 @@ public class EditEventFragment extends Fragment implements EventHandler, OnColor EditEventHelper.CALENDARS_WHERE_WRITEABLE_VISIBLE, null /* selection args */, null /* sort order */); - // TOKEN_COLORS mHandler.startQuery(TOKEN_COLORS, null, Colors.CONTENT_URI, EditEventHelper.COLORS_PROJECTION, diff --git a/app/src/main/java/com/android/calendar/event/EditEventHelper.java b/app/src/main/java/com/android/calendar/event/EditEventHelper.java index 1c41a320d..2b68bfdff 100644 --- a/app/src/main/java/com/android/calendar/event/EditEventHelper.java +++ b/app/src/main/java/com/android/calendar/event/EditEventHelper.java @@ -43,7 +43,6 @@ import com.android.calendar.CalendarEventModel; import com.android.calendar.CalendarEventModel.Attendee; import com.android.calendar.CalendarEventModel.ReminderEntry; import com.android.calendar.Utils; -import com.android.calendarcommon2.BuildConfig; import com.android.calendarcommon2.DateException; import com.android.calendarcommon2.EventRecurrence; import com.android.calendarcommon2.RecurrenceProcessor; @@ -61,7 +60,7 @@ import java.util.TimeZone; public class EditEventHelper { private static final String TAG = "EditEventHelper"; - private static final boolean DEBUG = true; // FIXME: 21/08/2024 Change to false when testing done + private static final boolean DEBUG = false; // Used for parsing rrules for special cases. private EventRecurrence mEventRecurrence = new EventRecurrence(); diff --git a/app/src/main/java/com/android/calendar/event/EditEventView.java b/app/src/main/java/com/android/calendar/event/EditEventView.java index 98bc65d16..c001c8b96 100644 --- a/app/src/main/java/com/android/calendar/event/EditEventView.java +++ b/app/src/main/java/com/android/calendar/event/EditEventView.java @@ -1632,13 +1632,11 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa public static class CalendarsAdapter extends ResourceCursorAdapter { public CalendarsAdapter(Context context, int resourceId, Cursor c) { super(context, resourceId, c); - Log.d("FAHIM", "Call CalendarsAdapter's constructor"); setDropDownViewResource(R.layout.calendars_dropdown_item); } @Override public void bindView(View view, Context context, Cursor cursor) { - Log.d("FAHIM", "Call CalendarsAdapter's bindView with cursor: "+cursor.getCount()); View colorBar = view.findViewById(R.id.color); int colorColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR); int nameColumn = cursor.getColumnIndexOrThrow(Calendars.CALENDAR_DISPLAY_NAME); diff --git a/app/src/main/java/com/android/calendar/icalendar/IcalendarUtils.java b/app/src/main/java/com/android/calendar/icalendar/IcalendarUtils.java index 0657d7c1f..8473cb385 100644 --- a/app/src/main/java/com/android/calendar/icalendar/IcalendarUtils.java +++ b/app/src/main/java/com/android/calendar/icalendar/IcalendarUtils.java @@ -140,25 +140,6 @@ public class IcalendarUtils { return calendar; } - public static int getEventCountFromCalendarFile(Context context, Uri uri) { - /* - TODO alternative method to avoid parsing everything: - loop through each line in the file - count "BEGIN:VEVENT" number - count "END:VEVENT" number - if event doesn't match : invalid file - return VEVENT counter - - CHECK the VCalendar.populateFromString for idea - */ - VCalendar calendar = readCalendarFromFile(context, uri); - if (calendar != null) - return calendar.getAllEvents().size(); - else return 0; - } - - - public static ArrayList getStringArrayFromFile(Context context, Uri uri) { String scheme = uri.getScheme(); InputStream inputStream = null; diff --git a/app/src/main/res/layout/import_activity.xml b/app/src/main/res/layout/import_activity.xml deleted file mode 100644 index 135440848..000000000 --- a/app/src/main/res/layout/import_activity.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - -- GitLab From a30e3148e5d3b400cd4e982973b10fafceef4b9c Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Thu, 22 Aug 2024 12:09:39 +0600 Subject: [PATCH 29/45] refactor: remove log statements --- .../com/android/calendar/AllInOneActivity.java | 1 - .../event/CalendarPickerDialogFragment.java | 15 +-------------- .../event/CalendarPickerQueryHandler.java | 6 +----- .../event/CalendarPickerQueryListener.java | 2 +- 4 files changed, 3 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 0ff58774a..3353d9b23 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -1563,7 +1563,6 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH @Override public void onCalendarPicked(long selectedCalendarId, String ownerAccount, String syncAccountName) { - Log.d(TAG, "onCalendarPicked: selectedCalendarId = " + selectedCalendarId); saveEventsInPickedCalendar(selectedCalendarId, ownerAccount, syncAccountName); } diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index 622891363..13eaf21fd 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -8,7 +8,6 @@ import android.content.Context; import android.database.Cursor; import android.os.Bundle; import android.provider.CalendarContract; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.AdapterView; @@ -28,7 +27,6 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap public static final String FRAGMENT_TAG = "PICK_CALENDAR_DIALOG"; public static final int SPINNER_DEFAULT_POSITION = 0; - private static final String TAG = CalendarPickerDialogFragment.class.getName(); private static final int TOKEN_CALENDARS = 8; private static final int EVENT_INDEX_OWNER_ACCOUNT = 2; private static final int EVENT_INDEX_ACCOUNT_NAME = 11; @@ -69,7 +67,6 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap @Override public void onCreate(@Nullable Bundle savedInstanceState) { - Log.d(TAG, "onCreate: bundle = " + savedInstanceState); super.onCreate(savedInstanceState); initiateCalendarQueryHandler(); @@ -78,7 +75,6 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap if (savedInstanceState != null) { mSelectedSpinnerItemPosition = savedInstanceState.getInt(BUNDLE_KEY_RESTORE_SELECTED_SPINNER_ITEM_POSITION); mNumberOfEvents = savedInstanceState.getInt(BUNDLE_KEY_RESTORE_NUMBER_OF_EVENTS); - Log.d(TAG, "onCreate: restored spinner position to " + mSelectedSpinnerItemPosition); } } @@ -87,12 +83,10 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap super.onSaveInstanceState(outState); outState.putInt(BUNDLE_KEY_RESTORE_SELECTED_SPINNER_ITEM_POSITION, mSelectedSpinnerItemPosition); outState.putInt(BUNDLE_KEY_RESTORE_NUMBER_OF_EVENTS, mNumberOfEvents); - Log.d(TAG, "onSaveInstanceState: saved spinner position to " + mSelectedSpinnerItemPosition); } @Override public void onDestroyView() { - Log.d(TAG, "onDestroyView: "); closeCursor(); super.onDestroyView(); } @@ -100,8 +94,6 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - Log.d(TAG, "onCreateDialog: "); - // onCreateDialog() gets called before onCreateView(), so the view needs to be created and supplied here // getView() returns null at this point View view = LayoutInflater.from(requireContext()).inflate(R.layout.pick_calendar_dialog, null); @@ -126,7 +118,6 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap private void closeCursor() { if (mCalendarsCursor != null && !mCalendarsCursor.isClosed()) { - Log.d(TAG, "closing cursor"); mCalendarsCursor.close(); } } @@ -152,8 +143,6 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap mOwnerAccount = mCalendarsCursor.getString(EVENT_INDEX_OWNER_ACCOUNT); mSyncAccountName = mCalendarsCursor.getString(EVENT_INDEX_ACCOUNT_NAME); bindCalendarSpinnerUi(); - } else { - Log.i(TAG, "Cursor doesn't have any calendar data."); } } @@ -173,19 +162,17 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { - Log.d(TAG, "onItemSelected: id: " + id + ", position: " + position); mSelectedSpinnerItemPosition = position; mPickedCalendarId = id; } @Override public void onNothingSelected(AdapterView parent) { - Log.d(TAG, "onNothingSelected"); mPickedCalendarId = -1; } @Override - public void onCursorAvailable(Cursor cursor, boolean userVisible) { + public void onCursorAvailable(Cursor cursor) { setCalendarsCursor(cursor); } diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java index 2571ceaae..bf069b64d 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java @@ -14,7 +14,6 @@ import com.android.calendar.Utils; import java.lang.ref.WeakReference; public class CalendarPickerQueryHandler extends AsyncQueryHandler { - private static final String TAG = CalendarPickerQueryHandler.class.getSimpleName(); private final WeakReference activityReference; private final WeakReference listenerReference; @@ -44,10 +43,7 @@ public class CalendarPickerQueryHandler extends AsyncQueryHandler { try (cursor) { MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); - if (DEBUG) { - Log.d(TAG, "onQueryComplete: found cursor with " + matrixCursor.getCount() + " calendars"); - } - queryListener.onCursorAvailable(matrixCursor, true); + queryListener.onCursorAvailable(matrixCursor); } } } diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java index 325431990..b6485952b 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java @@ -3,7 +3,7 @@ package com.android.calendar.event; import android.database.Cursor; public interface CalendarPickerQueryListener { - void onCursorAvailable(Cursor cursor, boolean userVisible); + void onCursorAvailable(Cursor cursor); void onCursorUnavailable(); -- GitLab From 910e8ddbdecd8ed2a67724e12ddaf8cf6b9a2207 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 22 Aug 2024 08:59:16 +0200 Subject: [PATCH 30/45] task: reintroduce usage of package name in manifest --- app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1e25cea32..fbef430a0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,6 +19,7 @@ --> -- GitLab From b0365e6b486efd909f9babec497f58387f2f5f3a Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Thu, 22 Aug 2024 13:00:52 +0600 Subject: [PATCH 31/45] refactor: navigate to first event's occurrence while importing events --- .../java/com/android/calendar/AllInOneActivity.java | 12 ++++++++++-- app/src/main/res/values/strings.xml | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 3353d9b23..ecae71405 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -1585,8 +1585,7 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH @Override public void onCalendarPickerError() { - // TODO: 21/08/2024 Use localized strings - Toast.makeText(this, "Couldn't add events to the calendar", Toast.LENGTH_SHORT).show(); + Toast.makeText(this, R.string.pick_calendar_error_importing_events, Toast.LENGTH_SHORT).show(); } private void saveEventsInPickedCalendar(long selectedCalendarId, String ownerAccount, String syncAccountName) { @@ -1610,9 +1609,18 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH } 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); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 990246595..c07c81458 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -208,6 +208,9 @@ In which calendar do you want to import these %d events? The events have been created successfully. + + Couldn\'t add events to the calendar. + -- GitLab From 61ca7e0a64508416534511a3927d6e8a03d9cfc6 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Thu, 22 Aug 2024 13:39:20 +0600 Subject: [PATCH 32/45] fix: improve spacing on both sides if the calendar name is too long --- app/src/main/res/layout/calendars_dropdown_item.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/calendars_dropdown_item.xml b/app/src/main/res/layout/calendars_dropdown_item.xml index fee8b81fc..8b3602241 100644 --- a/app/src/main/res/layout/calendars_dropdown_item.xml +++ b/app/src/main/res/layout/calendars_dropdown_item.xml @@ -15,6 +15,7 @@ --> + tools:text="@tools:sample/lorem/random" + /> + tools:text="@tools:sample/lorem/random" + /> -- GitLab From 11cddd3654c78f19bec8b7db9e7285e7a39b0e40 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 22 Aug 2024 09:25:38 +0200 Subject: [PATCH 33/45] task: replace raw string by value in strings.xml --- app/src/main/java/com/android/calendar/AllInOneActivity.java | 4 +--- app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index ecae71405..500d5a7c4 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -1601,9 +1601,7 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH if (editEventHelper.saveEvent(event, null, 0, null)) { addedEventcounter++; } else { - // FIXME 20/08/2024 is it the expected behaviour ? check & update - // Replace raw string by value from res folder - Toast.makeText(this, "Failed to add " + event.mTitle + " to the calendar", + Toast.makeText(this, getString(R.string.import_multi_event_single_event_failure_toast, event.mTitle), Toast.LENGTH_SHORT).show(); } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c07c81458..b212f230d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -208,6 +208,8 @@ In which calendar do you want to import these %d events? The events have been created successfully. + + Importing %s in your calendar failed Couldn\'t add events to the calendar. -- GitLab From 502fd190b5bd82d1d287310db50eed144ae34036 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 22 Aug 2024 10:19:15 +0200 Subject: [PATCH 34/45] task: add or update licence headers --- .../com/android/calendar/AllInOneActivity.java | 1 + .../android/calendar/CalendarEventModel.java | 1 + .../java/com/android/calendar/EventUtils.java | 17 +++++++++++++++++ .../com/android/calendar/ImportActivity.java | 17 +++++++++++++++++ .../calendar/event/CalendarPickerAdapter.java | 17 +++++++++++++++++ .../event/CalendarPickerDialogFragment.java | 17 +++++++++++++++++ .../event/CalendarPickerQueryHandler.java | 17 +++++++++++++++++ .../event/CalendarPickerQueryListener.java | 17 +++++++++++++++++ .../main/res/layout/pick_calendar_dialog.xml | 15 +++++++++++++++ app/src/main/res/values/strings.xml | 1 + 10 files changed, 120 insertions(+) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 500d5a7c4..04caa6eb7 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -1,6 +1,7 @@ /* * 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. diff --git a/app/src/main/java/com/android/calendar/CalendarEventModel.java b/app/src/main/java/com/android/calendar/CalendarEventModel.java index bf6092a85..d3e4d7035 100644 --- a/app/src/main/java/com/android/calendar/CalendarEventModel.java +++ b/app/src/main/java/com/android/calendar/CalendarEventModel.java @@ -1,5 +1,6 @@ /* * 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 diff --git a/app/src/main/java/com/android/calendar/EventUtils.java b/app/src/main/java/com/android/calendar/EventUtils.java index c66e4acee..44e9a5cdd 100644 --- a/app/src/main/java/com/android/calendar/EventUtils.java +++ b/app/src/main/java/com/android/calendar/EventUtils.java @@ -1,3 +1,20 @@ +/* + * 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 . + * + */ package com.android.calendar; import android.content.Context; diff --git a/app/src/main/java/com/android/calendar/ImportActivity.java b/app/src/main/java/com/android/calendar/ImportActivity.java index 5a8712f41..13acb3e4e 100644 --- a/app/src/main/java/com/android/calendar/ImportActivity.java +++ b/app/src/main/java/com/android/calendar/ImportActivity.java @@ -1,3 +1,20 @@ +/* + * 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 . + * + */ package com.android.calendar; import android.app.Activity; diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java b/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java index cb90832cf..79584d2b7 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java @@ -1,3 +1,20 @@ +/* + * 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 . + * + */ package com.android.calendar.event; import android.content.Context; diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index 13eaf21fd..03688d953 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -1,3 +1,20 @@ +/* + * 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 . + * + */ package com.android.calendar.event; diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java index bf069b64d..ce22ef3d7 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java @@ -1,3 +1,20 @@ +/* + * 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 . + * + */ package com.android.calendar.event; import static ws.xsoh.etar.BuildConfig.DEBUG; diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java index b6485952b..fad5104b9 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryListener.java @@ -1,3 +1,20 @@ +/* + * 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 . + * + */ package com.android.calendar.event; import android.database.Cursor; diff --git a/app/src/main/res/layout/pick_calendar_dialog.xml b/app/src/main/res/layout/pick_calendar_dialog.xml index ef04207f8..060217656 100644 --- a/app/src/main/res/layout/pick_calendar_dialog.xml +++ b/app/src/main/res/layout/pick_calendar_dialog.xml @@ -1,4 +1,19 @@ + -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 . ---> Date: Mon, 26 Aug 2024 07:16:59 +0000 Subject: [PATCH 37/45] task: code cleaning add a missing break line --- app/src/main/java/com/android/calendar/AllInOneActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 04caa6eb7..fb40f845d 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -1606,6 +1606,7 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH Toast.LENGTH_SHORT).show(); } } + if (addedEventcounter > 0) { Toast.makeText(this, getString(R.string.import_multi_event_confirmation_toast), Toast.LENGTH_SHORT).show(); navigateToFirstEventOccurrence(); -- GitLab From 3e0b8eaa81607ed4eff081a1d73280dcf04d370a Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 26 Aug 2024 16:08:07 +0200 Subject: [PATCH 38/45] refactor: eventUtils.getLocalTimeFromString() --- .../java/com/android/calendar/EventUtils.java | 159 ++++++++++-------- 1 file changed, 86 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/com/android/calendar/EventUtils.java b/app/src/main/java/com/android/calendar/EventUtils.java index 95599830a..bd847a28e 100644 --- a/app/src/main/java/com/android/calendar/EventUtils.java +++ b/app/src/main/java/com/android/calendar/EventUtils.java @@ -18,6 +18,7 @@ package com.android.calendar; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.os.Build; @@ -28,6 +29,7 @@ import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.calendar.event.EditEventActivity; import com.android.calendar.event.ExtendedProperty; @@ -125,89 +127,100 @@ public class EventUtils { 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 + private static long getLocalTimeFromString(@Nullable String iCalDate, @Nullable String iCalDateParam, @NonNull Context context) { + long result; 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); - } + result = parseUtcDate(iCalDate); + } else if (isTimeZoneReference(iCalDateParam)) { + result = parseDateWithTimeZone(iCalDate, iCalDateParam, context); + } else if (isDateOnly(iCalDateParam)) { + result = parseDateOnly(iCalDate); + } else { + result = parseLocalDate(iCalDate); } - // 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); - } + if (result == -1) { + Toast.makeText( + context, + context.getString(R.string.cal_import_error_date_msg, iCalDate), + Toast.LENGTH_SHORT).show(); + return System.currentTimeMillis(); } - // ONLY DATE, e.g. 20190415 - else if (iCalDateParam != null && iCalDateParam.equals("VALUE=DATE")) { - SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); - format.setTimeZone(TimeZone.getDefault()); + return result; + } - 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()); + private static long parseUtcDate(@Nullable String iCalDate) { + final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return parseDate(format, iCalDate, TimeZone.getDefault()); + } - try { - format.parse(iCalDate); - return format.getCalendar().getTimeInMillis(); - } catch (ParseException exception) { - Log.e(TAG, "Can't parse iCalDate:", exception); - } + private static boolean isTimeZoneReference(@Nullable String iCalDateParam) { + return iCalDateParam != null && iCalDateParam.startsWith("TZID="); + } + + private static long parseDateWithTimeZone(@NonNull String iCalDate, @Nullable String iCalDateParam, @NonNull Context context) { + final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); + final String timeZone = extractTimeZone(iCalDateParam); + setTimeZone(format, timeZone, context); + return parseDate(format, iCalDate, null); + } + + @NonNull + private static String extractTimeZone(@NonNull String iCalDateParam) { + if (iCalDateParam.length() < 5) return ""; + + return iCalDateParam.substring(5).replace("\"", ""); + } + + private static void setTimeZone(@NonNull SimpleDateFormat format, @NonNull String timeZone, @NonNull Context context) { + if (Arrays.asList(TimeZone.getAvailableIDs()).contains(timeZone)) { + format.setTimeZone(TimeZone.getTimeZone(timeZone)); + } else { + handleUnknownTimeZone(format, timeZone, context); } + } - Toast.makeText(context, context.getString(R.string.cal_import_error_date_msg, iCalDate), Toast.LENGTH_SHORT).show(); + private static void handleUnknownTimeZone(@NonNull SimpleDateFormat format, String timeZone, @NonNull Context context) { + final String convertedTimeZoneId = android.icu.util.TimeZone.getIDForWindowsID(timeZone, "001"); + if (convertedTimeZoneId != null && !convertedTimeZoneId.isEmpty()) { + format.setTimeZone(TimeZone.getTimeZone(convertedTimeZoneId)); + } else { + Toast.makeText( + context, + context.getString(R.string.cal_import_error_time_zone_msg, timeZone), + Toast.LENGTH_SHORT).show(); + } + } - return System.currentTimeMillis(); + private static boolean isDateOnly(String iCalDateParam) { + return iCalDateParam != null && iCalDateParam.equals("VALUE=DATE"); + } + + private static long parseDateOnly(@Nullable String iCalDate) { + final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); + format.setTimeZone(TimeZone.getDefault()); + return parseDate(format, iCalDate, null); + } + + private static long parseLocalDate(@Nullable String iCalDate) { + final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); + format.setTimeZone(TimeZone.getDefault()); + return parseDate(format, iCalDate, null); + } + + private static long parseDate(@NonNull SimpleDateFormat format, @Nullable String date, @Nullable TimeZone timeZone) { + try { + format.parse(date); + if (timeZone != null) { + format.setTimeZone(timeZone); + } + return format.getCalendar().getTimeInMillis(); + } catch (ParseException exception) { + Log.e(TAG, "Can't parse iCalDate:", exception); + return -1; // Return a default value or handle it as needed + } } } -- GitLab From 784051706670a5de9f01876dea44ad7542a5f26a Mon Sep 17 00:00:00 2001 From: Vincent Bourgmayer Date: Mon, 26 Aug 2024 14:19:12 +0000 Subject: [PATCH 39/45] Apply 1 suggestion(s) to 1 file(s) Co-authored-by: Sayantan Roychowdhury --- app/src/main/java/com/android/calendar/EventUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/android/calendar/EventUtils.java b/app/src/main/java/com/android/calendar/EventUtils.java index bd847a28e..f9a868625 100644 --- a/app/src/main/java/com/android/calendar/EventUtils.java +++ b/app/src/main/java/com/android/calendar/EventUtils.java @@ -96,7 +96,7 @@ public class EventUtils { } boolean isAllDay = getLocalTimeFromString(dtEnd, dtEndParam, context) - - getLocalTimeFromString(dtStart, dtStartParam, context) == 86400000; + - getLocalTimeFromString(dtStart, dtStartParam, context) == 24*60*60*1000; if (isTimeStartOfDay(dtStart, dtStartParam, context)) { -- GitLab From 088a1a18c020dc69f39fc99fe42943fcb0d49758 Mon Sep 17 00:00:00 2001 From: Vincent Bourgmayer Date: Mon, 26 Aug 2024 14:23:21 +0000 Subject: [PATCH 40/45] refactor: apply review suggestion: break one line command to make parameter more readable --- .../java/com/android/calendar/AllInOneActivity.java | 7 +++++-- .../calendar/event/CalendarPickerAdapter.java | 7 ++++++- .../event/CalendarPickerDialogFragment.java | 13 +++++++++---- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index fb40f845d..7b9f80620 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -1602,8 +1602,11 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH 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(); + Toast.makeText( + this, + getString(R.string.import_multi_event_single_event_failure_toast, event.mTitle), + Toast.LENGTH_SHORT + ).show(); } } diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java b/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java index 531e2f59a..6394a8987 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java @@ -41,7 +41,12 @@ public class CalendarPickerAdapter extends ResourceCursorAdapter { View colorBar = view.findViewById(R.id.color); int colorColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_COLOR); if (colorBar != null) { - colorBar.setBackgroundColor(Utils.getDisplayColorFromColor(context, cursor.getInt(colorColumn))); + colorBar.setBackgroundColor( + Utils.getDisplayColorFromColor( + context, + cursor.getInt(colorColumn) + ) + ); } int nameColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME); diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java index d10349734..0f7ca9c7a 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerDialogFragment.java @@ -148,10 +148,15 @@ public class CalendarPickerDialogFragment extends DialogFragment implements Adap CalendarPickerQueryHandler calendarPickerQueryHandler = new CalendarPickerQueryHandler(contentResolver, requireActivity(), this); - calendarPickerQueryHandler.startQuery(TOKEN_CALENDARS, null, CalendarContract.Calendars.CONTENT_URI, - EditEventHelper.CALENDARS_PROJECTION, - EditEventHelper.CALENDARS_WHERE_WRITEABLE_VISIBLE, null /* selection args */, - null /* sort order */); + calendarPickerQueryHandler.startQuery( + TOKEN_CALENDARS, + null, + CalendarContract.Calendars.CONTENT_URI, + EditEventHelper.CALENDARS_PROJECTION, + EditEventHelper.CALENDARS_WHERE_WRITEABLE_VISIBLE, + null /* selection args */, + null /* sort order */ + ); } public void setCalendarsCursor(Cursor cursor) { -- GitLab From e27b506c668b2e2436d0f17ef9bde3d146610b47 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 26 Aug 2024 16:39:46 +0200 Subject: [PATCH 41/45] refactor: apply Sayantan suggestion: invert if logic to remove brackets --- .../calendar/event/CalendarPickerAdapter.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java b/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java index 6394a8987..f03d5bdcb 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java @@ -53,15 +53,13 @@ public class CalendarPickerAdapter extends ResourceCursorAdapter { int ownerColumn = cursor.getColumnIndexOrThrow(CalendarContract.Calendars.OWNER_ACCOUNT); TextView name = view.findViewById(R.id.calendar_name); - if (name != null) { - String displayName = cursor.getString(nameColumn); - name.setText(displayName); + if (name == null) return; + String displayName = cursor.getString(nameColumn); + name.setText(displayName); - TextView accountName = view.findViewById(R.id.account_name); - if (accountName != null) { - accountName.setText(cursor.getString(ownerColumn)); - accountName.setVisibility(TextView.VISIBLE); - } - } + TextView accountName = view.findViewById(R.id.account_name); + if (accountName != null) return; + accountName.setText(cursor.getString(ownerColumn)); + accountName.setVisibility(TextView.VISIBLE); } } -- GitLab From e283bc6f6cf6b81fa63cf3211c0f09da23aaf517 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 26 Aug 2024 16:57:39 +0200 Subject: [PATCH 42/45] refactor: change indentation of few lines of code and add line break --- .../main/java/com/android/calendar/AllInOneActivity.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/android/calendar/AllInOneActivity.java b/app/src/main/java/com/android/calendar/AllInOneActivity.java index 7b9f80620..9b47acbc1 100644 --- a/app/src/main/java/com/android/calendar/AllInOneActivity.java +++ b/app/src/main/java/com/android/calendar/AllInOneActivity.java @@ -1574,13 +1574,15 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH 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) + 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(); } -- GitLab From 34097f63655b16108e4bba07a21887b5829abb3e Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Tue, 27 Aug 2024 12:21:37 +0600 Subject: [PATCH 43/45] fix: do null check for queryListener in WeakReference --- .../android/calendar/event/CalendarPickerQueryHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java index 83e56c712..ac8704651 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerQueryHandler.java @@ -42,7 +42,11 @@ public class CalendarPickerQueryHandler extends AsyncQueryHandler { protected void onQueryComplete(int token, Object cookie, Cursor cursor) { super.onQueryComplete(token, cookie, cursor); - CalendarPickerQueryListener queryListener = listenerReference.get(); + final CalendarPickerQueryListener queryListener = listenerReference.get(); + + if (queryListener == null) { + return; + } if (cursor == null || cursor.getCount() == 0) { queryListener.onCursorUnavailable(); -- GitLab From a6a273f24f32e4e7745381dc464fc2c92b7468eb Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 28 Aug 2024 09:19:12 +0200 Subject: [PATCH 44/45] Revert "refactor: eventUtils.getLocalTimeFromString()" This reverts commit 3e0b8eaa81607ed4eff081a1d73280dcf04d370a. --- .../java/com/android/calendar/EventUtils.java | 159 ++++++++---------- 1 file changed, 73 insertions(+), 86 deletions(-) diff --git a/app/src/main/java/com/android/calendar/EventUtils.java b/app/src/main/java/com/android/calendar/EventUtils.java index f9a868625..4fc170ffe 100644 --- a/app/src/main/java/com/android/calendar/EventUtils.java +++ b/app/src/main/java/com/android/calendar/EventUtils.java @@ -18,7 +18,6 @@ package com.android.calendar; -import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.os.Build; @@ -29,7 +28,6 @@ import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.android.calendar.event.EditEventActivity; import com.android.calendar.event.ExtendedProperty; @@ -127,100 +125,89 @@ public class EventUtils { return false; } - private static long getLocalTimeFromString(@Nullable String iCalDate, @Nullable String iCalDateParam, @NonNull Context context) { - long result; + 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")) { - result = parseUtcDate(iCalDate); - } else if (isTimeZoneReference(iCalDateParam)) { - result = parseDateWithTimeZone(iCalDate, iCalDateParam, context); - } else if (isDateOnly(iCalDateParam)) { - result = parseDateOnly(iCalDate); - } else { - result = parseLocalDate(iCalDate); + 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); + } } - if (result == -1) { - Toast.makeText( - context, - context.getString(R.string.cal_import_error_date_msg, iCalDate), - Toast.LENGTH_SHORT).show(); - return System.currentTimeMillis(); + // 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); + } } - return result; - } - - - private static long parseUtcDate(@Nullable String iCalDate) { - final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); - format.setTimeZone(TimeZone.getTimeZone("UTC")); - return parseDate(format, iCalDate, TimeZone.getDefault()); - } - - private static boolean isTimeZoneReference(@Nullable String iCalDateParam) { - return iCalDateParam != null && iCalDateParam.startsWith("TZID="); - } - - private static long parseDateWithTimeZone(@NonNull String iCalDate, @Nullable String iCalDateParam, @NonNull Context context) { - final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); - final String timeZone = extractTimeZone(iCalDateParam); - setTimeZone(format, timeZone, context); - return parseDate(format, iCalDate, null); - } - - @NonNull - private static String extractTimeZone(@NonNull String iCalDateParam) { - if (iCalDateParam.length() < 5) return ""; + // ONLY DATE, e.g. 20190415 + else if (iCalDateParam != null && iCalDateParam.equals("VALUE=DATE")) { + SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); + format.setTimeZone(TimeZone.getDefault()); - return iCalDateParam.substring(5).replace("\"", ""); - } - - private static void setTimeZone(@NonNull SimpleDateFormat format, @NonNull String timeZone, @NonNull Context context) { - if (Arrays.asList(TimeZone.getAvailableIDs()).contains(timeZone)) { - format.setTimeZone(TimeZone.getTimeZone(timeZone)); - } else { - handleUnknownTimeZone(format, timeZone, context); + try { + format.parse(iCalDate); + return format.getCalendar().getTimeInMillis(); + } catch (ParseException exception) { + Log.e(TAG, "Can't parse iCalDate:", exception); + } } - } - private static void handleUnknownTimeZone(@NonNull SimpleDateFormat format, String timeZone, @NonNull Context context) { - final String convertedTimeZoneId = android.icu.util.TimeZone.getIDForWindowsID(timeZone, "001"); - if (convertedTimeZoneId != null && !convertedTimeZoneId.isEmpty()) { - format.setTimeZone(TimeZone.getTimeZone(convertedTimeZoneId)); - } else { - Toast.makeText( - context, - context.getString(R.string.cal_import_error_time_zone_msg, timeZone), - Toast.LENGTH_SHORT).show(); - } - } + // FORM #1: DATE WITH LOCAL TIME, e.g. 19980118T230000 + else { + SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); + format.setTimeZone(TimeZone.getDefault()); - private static boolean isDateOnly(String iCalDateParam) { - return iCalDateParam != null && iCalDateParam.equals("VALUE=DATE"); - } - - private static long parseDateOnly(@Nullable String iCalDate) { - final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); - format.setTimeZone(TimeZone.getDefault()); - return parseDate(format, iCalDate, null); - } - - private static long parseLocalDate(@Nullable String iCalDate) { - final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); - format.setTimeZone(TimeZone.getDefault()); - return parseDate(format, iCalDate, null); - } - - private static long parseDate(@NonNull SimpleDateFormat format, @Nullable String date, @Nullable TimeZone timeZone) { - try { - format.parse(date); - if (timeZone != null) { - format.setTimeZone(timeZone); + try { + format.parse(iCalDate); + return format.getCalendar().getTimeInMillis(); + } catch (ParseException exception) { + Log.e(TAG, "Can't parse iCalDate:", exception); } - return format.getCalendar().getTimeInMillis(); - } catch (ParseException exception) { - Log.e(TAG, "Can't parse iCalDate:", exception); - return -1; // Return a default value or handle it as needed } + + Toast.makeText(context, context.getString(R.string.cal_import_error_date_msg, iCalDate), Toast.LENGTH_SHORT).show(); + + return System.currentTimeMillis(); } } -- GitLab From 189d82da2fe500f0512c8523a9d53c8757f455f2 Mon Sep 17 00:00:00 2001 From: Vincent Bourgmayer Date: Wed, 28 Aug 2024 09:56:44 +0000 Subject: [PATCH 45/45] fix: invert if statement to solve issue introduce in previous commit --- .../java/com/android/calendar/event/CalendarPickerAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java b/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java index f03d5bdcb..8c1e25ad2 100644 --- a/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java +++ b/app/src/main/java/com/android/calendar/event/CalendarPickerAdapter.java @@ -58,7 +58,7 @@ public class CalendarPickerAdapter extends ResourceCursorAdapter { name.setText(displayName); TextView accountName = view.findViewById(R.id.account_name); - if (accountName != null) return; + if (accountName == null) return; accountName.setText(cursor.getString(ownerColumn)); accountName.setVisibility(TextView.VISIBLE); } -- GitLab