merge from open-source master
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 80bb31d..064f8ae 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -24,9 +24,11 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.mail" />
<application android:name="CalendarApplication"
@@ -37,8 +39,7 @@
<uses-library android:name="android.test.runner" />
<activity android:name="LaunchActivity"
- android:theme="@android:style/Theme.Light"
- android:clearTaskOnLaunch="true">
+ android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
@@ -57,7 +58,7 @@
android:exported="true" />
<activity android:name="EditEvent" android:label="@string/event_edit_title"
- android:theme="@android:style/Theme.Light"
+ android:theme="@android:style/Theme"
android:configChanges="orientation|keyboardHidden">
<intent-filter>
@@ -77,6 +78,22 @@
<data android:mimeType="vnd.android.cursor.item/event" />
</intent-filter>
</activity>
+
+ <activity android:name="GoogleCalendarUriIntentFilter" android:label="@string/app_label"
+ android:theme="@android:style/Theme.Light"
+ android:configChanges="orientation|keyboardHidden">
+
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="http" android:host="www.google.com" android:pathPrefix="/calendar/event" />
+ <data android:scheme="https" android:host="www.google.com" android:pathPrefix="/calendar/event" />
+ <data android:scheme="http" android:host="www.google.com" android:pathPattern="/calendar/hosted/.*/event" />
+ <data android:scheme="https" android:host="www.google.com" android:pathPattern="/calendar/hosted/.*/event" />
+ </intent-filter>
+ </activity>
+
<activity android:name="SelectCalendarsActivity" android:label="@string/calendars_title" />
<activity android:name="CalendarPreferenceActivity" android:label="@string/preferences_title" />
<activity android:name="AlertActivity" android:launchMode="singleInstance"
diff --git a/res/drawable-hdpi-finger/btn_circle_disable.png b/res/drawable-hdpi-finger/btn_circle_disable.png
new file mode 100644
index 0000000..4f12078
--- /dev/null
+++ b/res/drawable-hdpi-finger/btn_circle_disable.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_disable_focused.png b/res/drawable-hdpi-finger/btn_circle_disable_focused.png
new file mode 100644
index 0000000..bc0d77b
--- /dev/null
+++ b/res/drawable-hdpi-finger/btn_circle_disable_focused.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_normal.png b/res/drawable-hdpi-finger/btn_circle_normal.png
new file mode 100644
index 0000000..d1e21d9
--- /dev/null
+++ b/res/drawable-hdpi-finger/btn_circle_normal.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_pressed.png b/res/drawable-hdpi-finger/btn_circle_pressed.png
new file mode 100644
index 0000000..31e69e2
--- /dev/null
+++ b/res/drawable-hdpi-finger/btn_circle_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_selected.png b/res/drawable-hdpi-finger/btn_circle_selected.png
new file mode 100644
index 0000000..0f809ee
--- /dev/null
+++ b/res/drawable-hdpi-finger/btn_circle_selected.png
Binary files differ
diff --git a/res/drawable-hdpi/app_icon.png b/res/drawable-hdpi/app_icon.png
new file mode 100644
index 0000000..d4cafaa
--- /dev/null
+++ b/res/drawable-hdpi/app_icon.png
Binary files differ
diff --git a/res/drawable-hdpi/bg_cal_card.9.png b/res/drawable-hdpi/bg_cal_card.9.png
new file mode 100644
index 0000000..bd76409
--- /dev/null
+++ b/res/drawable-hdpi/bg_cal_card.9.png
Binary files differ
diff --git a/res/drawable-hdpi/box_appointment_normal.9.png b/res/drawable-hdpi/box_appointment_normal.9.png
new file mode 100644
index 0000000..84d528c
--- /dev/null
+++ b/res/drawable-hdpi/box_appointment_normal.9.png
Binary files differ
diff --git a/res/drawable-hdpi/box_appointment_pressed.9.png b/res/drawable-hdpi/box_appointment_pressed.9.png
new file mode 100644
index 0000000..3dae23d
--- /dev/null
+++ b/res/drawable-hdpi/box_appointment_pressed.9.png
Binary files differ
diff --git a/res/drawable-hdpi/box_appointment_selected.9.png b/res/drawable-hdpi/box_appointment_selected.9.png
new file mode 100644
index 0000000..55eb437
--- /dev/null
+++ b/res/drawable-hdpi/box_appointment_selected.9.png
Binary files differ
diff --git a/res/drawable-hdpi/dna_1_of_6.png b/res/drawable-hdpi/dna_1_of_6.png
new file mode 100644
index 0000000..bcefba7
--- /dev/null
+++ b/res/drawable-hdpi/dna_1_of_6.png
Binary files differ
diff --git a/res/drawable-hdpi/dna_2345_of_6.png b/res/drawable-hdpi/dna_2345_of_6.png
new file mode 100644
index 0000000..0a2f1a8
--- /dev/null
+++ b/res/drawable-hdpi/dna_2345_of_6.png
Binary files differ
diff --git a/res/drawable-hdpi/dna_6_of_6.png b/res/drawable-hdpi/dna_6_of_6.png
new file mode 100644
index 0000000..c9d216e
--- /dev/null
+++ b/res/drawable-hdpi/dna_6_of_6.png
Binary files differ
diff --git a/res/drawable-hdpi/dna_empty.png b/res/drawable-hdpi/dna_empty.png
new file mode 100644
index 0000000..3b5e335
--- /dev/null
+++ b/res/drawable-hdpi/dna_empty.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_alarm_dark.png b/res/drawable-hdpi/ic_alarm_dark.png
new file mode 100644
index 0000000..272c489
--- /dev/null
+++ b/res/drawable-hdpi/ic_alarm_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_alarm_white.png b/res/drawable-hdpi/ic_alarm_white.png
new file mode 100644
index 0000000..7602182
--- /dev/null
+++ b/res/drawable-hdpi/ic_alarm_white.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_btn_round_minus.png b/res/drawable-hdpi/ic_btn_round_minus.png
new file mode 100644
index 0000000..02bec7d
--- /dev/null
+++ b/res/drawable-hdpi/ic_btn_round_minus.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_btn_round_plus.png b/res/drawable-hdpi/ic_btn_round_plus.png
new file mode 100644
index 0000000..1fdfb69
--- /dev/null
+++ b/res/drawable-hdpi/ic_btn_round_plus.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_picture.png b/res/drawable-hdpi/ic_contact_picture.png
new file mode 100755
index 0000000..7c34f5c
--- /dev/null
+++ b/res/drawable-hdpi/ic_contact_picture.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_reminder.png b/res/drawable-hdpi/ic_menu_reminder.png
new file mode 100644
index 0000000..64cf5f0
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_reminder.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_show_list.png b/res/drawable-hdpi/ic_menu_show_list.png
new file mode 100644
index 0000000..047cc12
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_show_list.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_repeat_dark.png b/res/drawable-hdpi/ic_repeat_dark.png
new file mode 100644
index 0000000..8402fa2
--- /dev/null
+++ b/res/drawable-hdpi/ic_repeat_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_repeat_white.png b/res/drawable-hdpi/ic_repeat_white.png
new file mode 100644
index 0000000..9a595fb
--- /dev/null
+++ b/res/drawable-hdpi/ic_repeat_white.png
Binary files differ
diff --git a/res/drawable-hdpi/im_avatar_picture_border_normal.9.png b/res/drawable-hdpi/im_avatar_picture_border_normal.9.png
new file mode 100755
index 0000000..2367f98
--- /dev/null
+++ b/res/drawable-hdpi/im_avatar_picture_border_normal.9.png
Binary files differ
diff --git a/res/drawable-hdpi/month_view_background.9.png b/res/drawable-hdpi/month_view_background.9.png
new file mode 100644
index 0000000..1108a78
--- /dev/null
+++ b/res/drawable-hdpi/month_view_background.9.png
Binary files differ
diff --git a/res/drawable-hdpi/month_view_longpress.9.png b/res/drawable-hdpi/month_view_longpress.9.png
new file mode 100644
index 0000000..aa8179d
--- /dev/null
+++ b/res/drawable-hdpi/month_view_longpress.9.png
Binary files differ
diff --git a/res/drawable-hdpi/month_view_pressed.9.png b/res/drawable-hdpi/month_view_pressed.9.png
new file mode 100644
index 0000000..0868dfd
--- /dev/null
+++ b/res/drawable-hdpi/month_view_pressed.9.png
Binary files differ
diff --git a/res/drawable-hdpi/month_view_selected.9.png b/res/drawable-hdpi/month_view_selected.9.png
new file mode 100644
index 0000000..e3236ad
--- /dev/null
+++ b/res/drawable-hdpi/month_view_selected.9.png
Binary files differ
diff --git a/res/drawable-hdpi/month_view_today_background.9.png b/res/drawable-hdpi/month_view_today_background.9.png
new file mode 100644
index 0000000..fbf1553
--- /dev/null
+++ b/res/drawable-hdpi/month_view_today_background.9.png
Binary files differ
diff --git a/res/drawable-hdpi/stat_notify_calendar.png b/res/drawable-hdpi/stat_notify_calendar.png
new file mode 100644
index 0000000..7e7cd60
--- /dev/null
+++ b/res/drawable-hdpi/stat_notify_calendar.png
Binary files differ
diff --git a/res/drawable-hdpi/sym_calendar_event.png b/res/drawable-hdpi/sym_calendar_event.png
new file mode 100644
index 0000000..3faeb88
--- /dev/null
+++ b/res/drawable-hdpi/sym_calendar_event.png
Binary files differ
diff --git a/res/drawable-land-hdpi/dna_1_of_6.png b/res/drawable-land-hdpi/dna_1_of_6.png
new file mode 100644
index 0000000..82747df
--- /dev/null
+++ b/res/drawable-land-hdpi/dna_1_of_6.png
Binary files differ
diff --git a/res/drawable-land-hdpi/dna_2345_of_6.png b/res/drawable-land-hdpi/dna_2345_of_6.png
new file mode 100644
index 0000000..cd68492
--- /dev/null
+++ b/res/drawable-land-hdpi/dna_2345_of_6.png
Binary files differ
diff --git a/res/drawable-land-hdpi/dna_6_of_6.png b/res/drawable-land-hdpi/dna_6_of_6.png
new file mode 100644
index 0000000..2c082a0
--- /dev/null
+++ b/res/drawable-land-hdpi/dna_6_of_6.png
Binary files differ
diff --git a/res/drawable-land-hdpi/dna_empty.png b/res/drawable-land-hdpi/dna_empty.png
new file mode 100644
index 0000000..26a8518
--- /dev/null
+++ b/res/drawable-land-hdpi/dna_empty.png
Binary files differ
diff --git a/res/drawable-land/dna_1_of_6.png b/res/drawable-land-mdpi/dna_1_of_6.png
similarity index 100%
rename from res/drawable-land/dna_1_of_6.png
rename to res/drawable-land-mdpi/dna_1_of_6.png
Binary files differ
diff --git a/res/drawable-land/dna_2345_of_6.png b/res/drawable-land-mdpi/dna_2345_of_6.png
similarity index 100%
rename from res/drawable-land/dna_2345_of_6.png
rename to res/drawable-land-mdpi/dna_2345_of_6.png
Binary files differ
diff --git a/res/drawable-land/dna_6_of_6.png b/res/drawable-land-mdpi/dna_6_of_6.png
similarity index 100%
rename from res/drawable-land/dna_6_of_6.png
rename to res/drawable-land-mdpi/dna_6_of_6.png
Binary files differ
diff --git a/res/drawable-land/dna_empty.png b/res/drawable-land-mdpi/dna_empty.png
similarity index 100%
rename from res/drawable-land/dna_empty.png
rename to res/drawable-land-mdpi/dna_empty.png
Binary files differ
diff --git a/res/drawable-finger/btn_circle_disable.png b/res/drawable-mdpi-finger/btn_circle_disable.png
similarity index 100%
rename from res/drawable-finger/btn_circle_disable.png
rename to res/drawable-mdpi-finger/btn_circle_disable.png
Binary files differ
diff --git a/res/drawable-finger/btn_circle_disable_focused.png b/res/drawable-mdpi-finger/btn_circle_disable_focused.png
similarity index 100%
rename from res/drawable-finger/btn_circle_disable_focused.png
rename to res/drawable-mdpi-finger/btn_circle_disable_focused.png
Binary files differ
diff --git a/res/drawable-finger/btn_circle_normal.png b/res/drawable-mdpi-finger/btn_circle_normal.png
similarity index 100%
rename from res/drawable-finger/btn_circle_normal.png
rename to res/drawable-mdpi-finger/btn_circle_normal.png
Binary files differ
diff --git a/res/drawable-finger/btn_circle_pressed.png b/res/drawable-mdpi-finger/btn_circle_pressed.png
similarity index 100%
rename from res/drawable-finger/btn_circle_pressed.png
rename to res/drawable-mdpi-finger/btn_circle_pressed.png
Binary files differ
diff --git a/res/drawable-finger/btn_circle_selected.png b/res/drawable-mdpi-finger/btn_circle_selected.png
similarity index 100%
rename from res/drawable-finger/btn_circle_selected.png
rename to res/drawable-mdpi-finger/btn_circle_selected.png
Binary files differ
diff --git a/res/drawable/app_icon.png b/res/drawable-mdpi/app_icon.png
similarity index 100%
rename from res/drawable/app_icon.png
rename to res/drawable-mdpi/app_icon.png
Binary files differ
diff --git a/res/drawable/bg_cal_card.9.png b/res/drawable-mdpi/bg_cal_card.9.png
similarity index 100%
rename from res/drawable/bg_cal_card.9.png
rename to res/drawable-mdpi/bg_cal_card.9.png
Binary files differ
diff --git a/res/drawable/box_appointment_normal.9.png b/res/drawable-mdpi/box_appointment_normal.9.png
similarity index 100%
rename from res/drawable/box_appointment_normal.9.png
rename to res/drawable-mdpi/box_appointment_normal.9.png
Binary files differ
diff --git a/res/drawable/box_appointment_pressed.9.png b/res/drawable-mdpi/box_appointment_pressed.9.png
similarity index 100%
rename from res/drawable/box_appointment_pressed.9.png
rename to res/drawable-mdpi/box_appointment_pressed.9.png
Binary files differ
diff --git a/res/drawable/box_appointment_selected.9.png b/res/drawable-mdpi/box_appointment_selected.9.png
similarity index 100%
rename from res/drawable/box_appointment_selected.9.png
rename to res/drawable-mdpi/box_appointment_selected.9.png
Binary files differ
diff --git a/res/drawable/dna_1_of_6.png b/res/drawable-mdpi/dna_1_of_6.png
similarity index 100%
rename from res/drawable/dna_1_of_6.png
rename to res/drawable-mdpi/dna_1_of_6.png
Binary files differ
diff --git a/res/drawable/dna_2345_of_6.png b/res/drawable-mdpi/dna_2345_of_6.png
similarity index 100%
rename from res/drawable/dna_2345_of_6.png
rename to res/drawable-mdpi/dna_2345_of_6.png
Binary files differ
diff --git a/res/drawable/dna_6_of_6.png b/res/drawable-mdpi/dna_6_of_6.png
similarity index 100%
rename from res/drawable/dna_6_of_6.png
rename to res/drawable-mdpi/dna_6_of_6.png
Binary files differ
diff --git a/res/drawable/dna_empty.png b/res/drawable-mdpi/dna_empty.png
similarity index 100%
rename from res/drawable/dna_empty.png
rename to res/drawable-mdpi/dna_empty.png
Binary files differ
diff --git a/res/drawable/ic_alarm_dark.png b/res/drawable-mdpi/ic_alarm_dark.png
similarity index 100%
rename from res/drawable/ic_alarm_dark.png
rename to res/drawable-mdpi/ic_alarm_dark.png
Binary files differ
diff --git a/res/drawable/ic_alarm_white.png b/res/drawable-mdpi/ic_alarm_white.png
similarity index 100%
rename from res/drawable/ic_alarm_white.png
rename to res/drawable-mdpi/ic_alarm_white.png
Binary files differ
diff --git a/res/drawable/ic_btn_round_minus.png b/res/drawable-mdpi/ic_btn_round_minus.png
similarity index 100%
rename from res/drawable/ic_btn_round_minus.png
rename to res/drawable-mdpi/ic_btn_round_minus.png
Binary files differ
diff --git a/res/drawable/ic_btn_round_plus.png b/res/drawable-mdpi/ic_btn_round_plus.png
similarity index 100%
rename from res/drawable/ic_btn_round_plus.png
rename to res/drawable-mdpi/ic_btn_round_plus.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture.png b/res/drawable-mdpi/ic_contact_picture.png
new file mode 100644
index 0000000..3a338e8
--- /dev/null
+++ b/res/drawable-mdpi/ic_contact_picture.png
Binary files differ
diff --git a/res/drawable/ic_menu_reminder.png b/res/drawable-mdpi/ic_menu_reminder.png
similarity index 100%
rename from res/drawable/ic_menu_reminder.png
rename to res/drawable-mdpi/ic_menu_reminder.png
Binary files differ
diff --git a/res/drawable/ic_menu_show_list.png b/res/drawable-mdpi/ic_menu_show_list.png
similarity index 100%
rename from res/drawable/ic_menu_show_list.png
rename to res/drawable-mdpi/ic_menu_show_list.png
Binary files differ
diff --git a/res/drawable/ic_repeat_dark.png b/res/drawable-mdpi/ic_repeat_dark.png
similarity index 100%
rename from res/drawable/ic_repeat_dark.png
rename to res/drawable-mdpi/ic_repeat_dark.png
Binary files differ
diff --git a/res/drawable/ic_repeat_white.png b/res/drawable-mdpi/ic_repeat_white.png
similarity index 100%
rename from res/drawable/ic_repeat_white.png
rename to res/drawable-mdpi/ic_repeat_white.png
Binary files differ
diff --git a/res/drawable-mdpi/im_avatar_picture_border_normal.9.png b/res/drawable-mdpi/im_avatar_picture_border_normal.9.png
new file mode 100644
index 0000000..01cc9dc
--- /dev/null
+++ b/res/drawable-mdpi/im_avatar_picture_border_normal.9.png
Binary files differ
diff --git a/res/drawable/month_view_background.9.png b/res/drawable-mdpi/month_view_background.9.png
similarity index 100%
rename from res/drawable/month_view_background.9.png
rename to res/drawable-mdpi/month_view_background.9.png
Binary files differ
diff --git a/res/drawable/month_view_longpress.9.png b/res/drawable-mdpi/month_view_longpress.9.png
similarity index 100%
rename from res/drawable/month_view_longpress.9.png
rename to res/drawable-mdpi/month_view_longpress.9.png
Binary files differ
diff --git a/res/drawable/month_view_pressed.9.png b/res/drawable-mdpi/month_view_pressed.9.png
similarity index 100%
rename from res/drawable/month_view_pressed.9.png
rename to res/drawable-mdpi/month_view_pressed.9.png
Binary files differ
diff --git a/res/drawable/month_view_selected.9.png b/res/drawable-mdpi/month_view_selected.9.png
similarity index 100%
rename from res/drawable/month_view_selected.9.png
rename to res/drawable-mdpi/month_view_selected.9.png
Binary files differ
diff --git a/res/drawable/month_view_today_background.9.png b/res/drawable-mdpi/month_view_today_background.9.png
similarity index 100%
rename from res/drawable/month_view_today_background.9.png
rename to res/drawable-mdpi/month_view_today_background.9.png
Binary files differ
diff --git a/res/drawable/stat_notify_calendar.png b/res/drawable-mdpi/stat_notify_calendar.png
similarity index 100%
rename from res/drawable/stat_notify_calendar.png
rename to res/drawable-mdpi/stat_notify_calendar.png
Binary files differ
diff --git a/res/drawable/sym_calendar_event.png b/res/drawable-mdpi/sym_calendar_event.png
similarity index 100%
rename from res/drawable/sym_calendar_event.png
rename to res/drawable-mdpi/sym_calendar_event.png
Binary files differ
diff --git a/res/layout/agenda_header_footer.xml b/res/layout/agenda_header_footer.xml
new file mode 100644
index 0000000..96824f5
--- /dev/null
+++ b/res/layout/agenda_header_footer.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ 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 the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:gravity="center_vertical"
+ android:textColor="?android:attr/textColorSecondary"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ style="?android:attr/textAppearanceMediumInverse" />
diff --git a/res/layout/contact_item.xml b/res/layout/contact_item.xml
new file mode 100644
index 0000000..86ba140
--- /dev/null
+++ b/res/layout/contact_item.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ 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 the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:paddingLeft="0dip"
+ android:paddingRight="9dip"
+ android:minHeight="48dip">
+
+ <QuickContactBadge
+ android:id="@+id/badge"
+ android:paddingLeft="3dip"
+ android:paddingRight="3dip"
+ android:paddingTop="3dip"
+ android:paddingBottom="6dip"
+ android:layout_above="@+id/separator"
+ android:layout_marginLeft="2dip"
+ android:layout_marginRight="14dip"
+ android:layout_marginTop="4dip"
+ android:layout_marginBottom="3dip"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:src="@drawable/ic_contact_picture"
+ style="@*android:style/Widget.QuickContactBadgeSmall.WindowSmall" />
+
+ <TextView
+ android:id="@+id/name"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:inputType="none"
+ android:paddingLeft="2dip"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@id/badge"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ <ImageView
+ android:id="@+id/presence"
+ android:scaleType="fitXY"
+ android:visibility="gone"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <View
+ android:id="@+id/separator"
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:layout_alignParentBottom="true"
+ android:background="@android:drawable/divider_horizontal_bright" />
+
+</RelativeLayout>
diff --git a/res/layout/edit_event.xml b/res/layout/edit_event.xml
index 352a4ed..88b92b7 100644
--- a/res/layout/edit_event.xml
+++ b/res/layout/edit_event.xml
@@ -68,14 +68,14 @@
android:layout_height="wrap_content"
android:layout_weight="7"
android:gravity="left|center_vertical"
- style="?android:attr/textAppearanceMedium"/>
+ style="?android:attr/textAppearanceMediumInverse"/>
<Button android:id="@+id/start_time"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="4"
android:gravity="left|center_vertical"
- style="?android:attr/textAppearanceMedium"/>
+ style="?android:attr/textAppearanceMediumInverse"/>
</LinearLayout>
@@ -95,14 +95,14 @@
android:layout_height="wrap_content"
android:layout_weight="7"
android:gravity="left|center_vertical"
- style="?android:attr/textAppearanceMedium"/>
+ style="?android:attr/textAppearanceMediumInverse"/>
<Button android:id="@+id/end_time"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="4"
android:gravity="left|center_vertical"
- style="?android:attr/textAppearanceMedium"/>
+ style="?android:attr/textAppearanceMediumInverse"/>
</LinearLayout>
<LinearLayout
@@ -116,7 +116,7 @@
android:text="@string/edit_event_all_day_label"
android:paddingTop="1dip"
android:paddingRight="7dip"
- style="?android:attr/textAppearanceMedium"/>
+ style="?android:attr/textAppearanceMediumInverse"/>
<CheckBox android:id="@+id/is_all_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -192,7 +192,31 @@
<Spinner android:id="@+id/calendars"
android:layout_width="fill_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:prompt="@string/edit_event_calendar_label"/>
+ </LinearLayout>
+
+ <!-- GUESTS/ATTENDEES -->
+ <LinearLayout android:id="@+id/attendees_group"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@style/EditEvent_Layout">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/attendees_label"
+ style="@style/TextAppearance.EditEvent_Label"/>
+
+ <MultiAutoCompleteTextView android:id="@+id/attendees"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textEmailAddress|textMultiLine"
+ android:hint="@string/hint_attendees"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="6dip"
+ android:imeOptions="actionNext"/>
</LinearLayout>
<!-- REPEATS -->
@@ -210,7 +234,8 @@
<Spinner android:id="@+id/repeats"
android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content"
+ android:prompt="@string/repeats_label"/>
</LinearLayout>
<!-- MORE OPTIONS -->
@@ -236,7 +261,8 @@
<Spinner android:id="@+id/availability"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:entries="@array/availability" />
+ android:entries="@array/availability"
+ android:prompt="@string/presence_label"/>
</LinearLayout>
<!-- PRIVACY -->
@@ -255,7 +281,8 @@
<Spinner android:id="@+id/visibility"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:entries="@array/visibility" />
+ android:entries="@array/visibility"
+ android:prompt="@string/privacy_label"/>
</LinearLayout>
</LinearLayout>
@@ -294,7 +321,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="2dip"
- android:text="@string/add_new_reminder"/>
+ android:text="@string/add_new_reminder"
+ style="?android:attr/textAppearanceSmallInverse"/>
<ImageButton android:id="@+id/reminder_add"
style="@style/PlusButton"
diff --git a/res/layout/event_info_activity.xml b/res/layout/event_info_activity.xml
index 3992e80..a431919 100644
--- a/res/layout/event_info_activity.xml
+++ b/res/layout/event_info_activity.xml
@@ -119,7 +119,30 @@
android:autoLink="all"
style="?android:attr/textAppearanceSmall"
/>
+
+ <!-- Organizer -->
+ <LinearLayout android:id="@+id/organizer_container"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="5dip"
+ >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="5dip"
+ android:text="@string/view_event_organizer_label"
+ style="?android:attr/textAppearanceSmall"
+ />
+ <TextView android:id="@+id/organizer"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/textAppearanceSmall"
+ />
+ </LinearLayout>
+
<!-- DESCRIPTION -->
<TextView android:id="@+id/description"
android:layout_width="fill_parent"
@@ -154,7 +177,7 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>
-
+
<!-- RESPONSE -->
<LinearLayout android:id="@+id/response_container"
android:orientation="vertical"
@@ -169,7 +192,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/view_event_response_label"
- style="@style/TextAppearance.EditEvent_Label"/>
+ style="@style/TextAppearance.EventInfo_Label"/>
<Spinner android:id="@+id/response_value"
style="?android:attr/textAppearanceMedium"
@@ -179,6 +202,14 @@
android:entries="@array/response_labels1"/>
</LinearLayout>
+ <!-- GUEST LIST -->
+ <LinearLayout
+ android:id="@+id/attendee_list"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="8dip"
+ android:orientation="vertical" />
+
<!-- REMINDERS -->
<LinearLayout android:id="@+id/reminders_container"
android:orientation="vertical"
@@ -187,13 +218,13 @@
android:paddingLeft="8dip"
android:paddingRight="7dip"
android:paddingTop="5dip"
- android:paddingBottom="1dip">
+ android:paddingBottom="5dip">
<TextView android:id="@+id/reminders_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/reminders_label"
- style="@style/TextAppearance.EditEvent_Label"/>
+ style="@style/TextAppearance.EventInfo_Label"/>
<LinearLayout android:id="@+id/reminder_items_container"
style="?android:attr/textAppearanceMedium"
@@ -201,26 +232,23 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</LinearLayout>
- </LinearLayout>
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical|right"
- android:paddingRight="5dip"
- android:paddingBottom="5dip">
-
- <TextView
- android:layout_width="wrap_content"
+ <LinearLayout android:id="@+id/reminder_adder"
+ android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_marginRight="7dip"
- android:text="@string/add_new_reminder"/>
+ android:gravity="center_vertical|right">
- <ImageButton android:id="@+id/reminder_add"
- style="@style/PlusButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="2dip"
- />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="7dip"
+ android:text="@string/add_new_reminder"/>
+
+ <ImageButton android:id="@+id/reminder_add"
+ style="@style/PlusButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ </LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 5616b49..bd5a2ad 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Kalendář"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Vítá vás Kalendář Google!"</font>" "\n" Přístup společnosti Google ke správě vašeho časového plánu je založen na myšlence, že plánování událostí může být intuitivnější, efektivnější a přístupnější."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Vítá vás Kalendář Google!"</font>" "\n" Přístup společnosti Google ke správě vašeho časového plánu je založen na myšlence, že plánování událostí může být intuitivnější, efektivnější a přístupnější."</string>
<string name="what_label" msgid="1933578391985810049">"Co"</string>
<string name="when_label" msgid="894636567327261599">"Kdy"</string>
<string name="where_label" msgid="3472482206065595118">"Kde"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Hosté"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Opakování"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Bez předmětu)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Název události"</string>
<string name="hint_where" msgid="3116239630502213241">"Místo události"</string>
<string name="hint_description" msgid="4198202812240544553">"Popis události"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"E-mailové adresy"</string>
<string name="creating_event" msgid="8237877638457604455">"Vytváření události…"</string>
<string name="saving_event" msgid="8853425146353785688">"Ukládání události…"</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Načítání kalendářů"</string>
@@ -78,15 +81,19 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Zobrazit další možnosti"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Skrýt další možnosti"</string>
<string name="description_label" msgid="7193475046717505855">"Popis"</string>
- <string name="presence_label" msgid="2155381988163502023">"Účast"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Zobrazit mě jako"</string>
<string name="privacy_label" msgid="9215031965259597335">"Přístupnost"</string>
<string name="reminders_label" msgid="8345054160145333166">"Připomenutí"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Žádné kalendáře"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Nemáte žádné kalendáře."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Kalendář"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organizátor"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Místní časové pásmo"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Zúčastníte se?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Dnes"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Dnes, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Načítání..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Zobrazují se události od <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Další položky zobrazíte klepnutím."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Zobrazují se události do <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Další položky zobrazíte klepnutím."</string>
<string name="num_events" msgid="3351672964607162257">"Počet událostí"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Upravit událost"</string>
<string name="delete_label" msgid="874742811918030739">"Smazat"</string>
@@ -118,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Upozornění a oznámení"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Vibrace"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Vybrat vyzvánění"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Nastavit výchozí připomenutí"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Výchozí čas připomenutí"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Výchozí čas připomenutí"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 25e94be..94f7d59 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Kalender"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Velkommen til Google Kalender!"</font>" "\n"En tilgang til planlægning fra Google baseret på den forestilling, at planlægning af begivenheder kan være mere intuitivt, effektivt og lettilgængeligt."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Velkommen til Google Kalender!"</font>" "\n"En tilgang til planlægning fra Google baseret på den forestilling, at planlægning af begivenheder kan være mere intuitivt, effektivt og lettilgængeligt."</string>
<string name="what_label" msgid="1933578391985810049">"Hvad"</string>
<string name="when_label" msgid="894636567327261599">"Hvornår"</string>
<string name="where_label" msgid="3472482206065595118">"Hvor"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Gæster"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Gentagelse"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Intet emne)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Begivenhedens navn"</string>
<string name="hint_where" msgid="3116239630502213241">"Begivenhedens placering"</string>
<string name="hint_description" msgid="4198202812240544553">"Beskrivelse af begivenhed"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"E-mail-adresser"</string>
<string name="creating_event" msgid="8237877638457604455">"Opretter begivenhed ..."</string>
<string name="saving_event" msgid="8853425146353785688">"Gemmer begivenhed ..."</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Indlæser kalendere"</string>
@@ -78,23 +81,25 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Vis ekstra valgmuligheder"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Skjul ekstra valgmuligheder"</string>
<string name="description_label" msgid="7193475046717505855">"Beskrivelse"</string>
- <string name="presence_label" msgid="2155381988163502023">"Tilstedeværelse"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Vis mig som"</string>
<string name="privacy_label" msgid="9215031965259597335">"Fortrolighed"</string>
<string name="reminders_label" msgid="8345054160145333166">"Påmindelser"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Der er ingen kalendere"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Du har ingen kalendere."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Kalender"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organisator"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Lokal tidszone"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Deltager?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"I dag"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"I dag <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Indlæser ..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Viser begivenheder siden <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Tryk for at lede efter flere."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Viser begivenheder indtil <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Tryk for at lede efter flere."</string>
<string name="num_events" msgid="3351672964607162257">"Ant. beg."</string>
<string name="edit_event_label" msgid="2900418236819088363">"Rediger begivenhed"</string>
<string name="delete_label" msgid="874742811918030739">"Slet"</string>
<string name="delete_event_label" msgid="3738436215987360463">"Slet begivenhed"</string>
- <!-- no translation found for save_label (2133599104834432589) -->
- <skip />
- <!-- no translation found for discard_label (4510607554910139220) -->
- <skip />
+ <string name="save_label" msgid="2133599104834432589">"Færdig"</string>
+ <string name="discard_label" msgid="4510607554910139220">"Vend tilbage"</string>
<string name="import_label" msgid="6993796574741477222">"Importer"</string>
<string name="snooze_all_label" msgid="4994488813124409077">"Sæt alle i slumretilstand"</string>
<string name="dismiss_all_label" msgid="1834400025868091301">"Annuller alle"</string>
@@ -120,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Underretninger og meddelelser"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Vibrer"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Vælg ringetone"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Indstil standardpåmindelse"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Standard påmindelsestid"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Standard påmindelsestid"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 73b75c2..d10a0ac 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Kalender"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Willkommen bei Google Kalender!"</font>" "\n"Ein Ansatz à la Google beim Verwalten Ihrer Termine, der zeigen soll, dass Terminverwaltung intuitiver, effizienter und einfacher sein kann."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Willkommen bei Google Kalender!"</font>" "\n"Dies ist ein Ansatz von Google zum Verwalten Ihrer Termine, der auf dem Gedanken fußt, dass Terminverwaltung intuitiver, effizienter und einfacher sein kann."</string>
<string name="what_label" msgid="1933578391985810049">"Was"</string>
<string name="when_label" msgid="894636567327261599">"Wann"</string>
<string name="where_label" msgid="3472482206065595118">"Wo"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Gäste"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Wiederholung"</string>
<string name="no_title_label" msgid="7352118984125107858">"(kein Thema)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Terminname"</string>
<string name="hint_where" msgid="3116239630502213241">"Ort des Termins"</string>
<string name="hint_description" msgid="4198202812240544553">"Terminbeschreibung"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"E-Mail-Adressen"</string>
<string name="creating_event" msgid="8237877638457604455">"Termin wird erstellt…"</string>
<string name="saving_event" msgid="8853425146353785688">"Termin wird gespeichert…"</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Kalender werden geladen."</string>
@@ -78,15 +81,19 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Zusätzliche Optionen anzeigen"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Zusätzliche Optionen ausblenden"</string>
<string name="description_label" msgid="7193475046717505855">"Beschreibung"</string>
- <string name="presence_label" msgid="2155381988163502023">"Status"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Mich anzeigen als"</string>
<string name="privacy_label" msgid="9215031965259597335">"Datenschutz"</string>
<string name="reminders_label" msgid="8345054160145333166">"Erinnerungen"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Keine Kalender"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Sie haben keine Kalender."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Kalender"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organizer"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Lokalzeit"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Teilnahme?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Heute"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Heute, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Ladevorgang läuft..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Termine seit <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g> werden angezeigt. Tippen Sie, um die Suche zu erweitern."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Termine bis <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g> werden angezeigt. Tippen Sie, um die Suche zu erweitern."</string>
<string name="num_events" msgid="3351672964607162257">"Anzahl Termine"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Termin bearbeiten"</string>
<string name="delete_label" msgid="874742811918030739">"Löschen"</string>
@@ -118,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Benachrichtigungen und Hinweise"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Vibration"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Klingelton auswählen"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Standard-Erinnerungszeit"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Standard-Erinnerungszeit"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Standard-Erinnerungszeit"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-el/arrays.xml b/res/values-el/arrays.xml
index 1570f7d..e3ec320 100644
--- a/res/values-el/arrays.xml
+++ b/res/values-el/arrays.xml
@@ -67,7 +67,7 @@
<item msgid="5631923029483854900">"δεύτερο"</item>
<item msgid="148935502975250973">"τρίτο"</item>
<item msgid="5124200797326669172">"τέταρτο"</item>
- <item msgid="7564374178324652137">"τελευταίο"</item>
+ <item msgid="7564374178324652137">"τελευταία"</item>
</string-array>
<string-array name="response_labels1">
<item msgid="3654955385120106241">"(Καμία απάντηση)"</item>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 6eefad0..24da1a0 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Ημερολόγιο"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Καλώς ήρθατε στο Ημερολόγιο Google!"</font>" "\n"Μια πρόταση της Google για τη διαχείριση του προγράμματός σας βασισμένη στην ιδέα ότι ο προγραμματισμός συμβάντων μπορεί να είναι πιο διαισθητικός, πιο αποδοτικός και πιο προσιτός."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Καλώς ήρθατε στο Ημερολόγιο Google!"</font>" "\n"Μια πρόταση της Google για τη διαχείριση του προγράμματός σας βασισμένη στην ιδέα ότι ο προγραμματισμός συμβάντων μπορεί να γίνει πιο διαισθητικός, πιο αποδοτικός και πιο προσιτός."</string>
<string name="what_label" msgid="1933578391985810049">"Τι"</string>
<string name="when_label" msgid="894636567327261599">"Πότε"</string>
<string name="where_label" msgid="3472482206065595118">"Που"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Προσκεκλημένοι"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Επανάληψη"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Χωρίς θέμα)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Όνομα συμβάντος"</string>
<string name="hint_where" msgid="3116239630502213241">"Τοποθεσία συμβάντος"</string>
<string name="hint_description" msgid="4198202812240544553">"Περιγραφή συμβάντος"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"Διευθύνσεις ηλεκτρονικού ταχυδρομείου"</string>
<string name="creating_event" msgid="8237877638457604455">"Δημιουργία συμβάντος..."</string>
<string name="saving_event" msgid="8853425146353785688">"Αποθήκευση συμβάντος…"</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Φόρτωση ημερολογίων"</string>
@@ -78,23 +81,25 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Εμφάνιση επιπρόσθετων επιλογών"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Απόκρυψη επιπρόσθετων επιλογών"</string>
<string name="description_label" msgid="7193475046717505855">"Περιγραφή"</string>
- <string name="presence_label" msgid="2155381988163502023">"Παρουσία"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Να εμφανίζομαι ως"</string>
<string name="privacy_label" msgid="9215031965259597335">"Απόρρητο"</string>
<string name="reminders_label" msgid="8345054160145333166">"Υπενθυμίσεις"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Δεν υπάρχουν ημερολόγια"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Δεν έχετε ημερολόγια."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Ημερολόγιο"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Διοργανωτής"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Τοπική ζώνη ώρας"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Θα παραβρεθείτε;"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Σήμερα"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Σήμερα, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Φόρτωση..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Προβολή συμβάντων από <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Πατήστε για να αναζητήσετε περισσότερα συμβάντα."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Προβολή συμβάντων έως <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Αγγίξτε για να αναζητήσετε περισσότερα."</string>
<string name="num_events" msgid="3351672964607162257">"Αριθμ. συμβάντων"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Επεξεργασία συμβάντος"</string>
<string name="delete_label" msgid="874742811918030739">"Διαγραφή"</string>
<string name="delete_event_label" msgid="3738436215987360463">"Διαγραφή συμβάντος"</string>
- <!-- no translation found for save_label (2133599104834432589) -->
- <skip />
- <!-- no translation found for discard_label (4510607554910139220) -->
- <skip />
+ <string name="save_label" msgid="2133599104834432589">"Τέλος"</string>
+ <string name="discard_label" msgid="4510607554910139220">"Επαναφορά"</string>
<string name="import_label" msgid="6993796574741477222">"Εισαγωγή"</string>
<string name="snooze_all_label" msgid="4994488813124409077">"Αναβολή όλων"</string>
<string name="dismiss_all_label" msgid="1834400025868091301">"Παράβλεψη όλων"</string>
@@ -120,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Προειδοποιήσεις & ειδοποιήσεις"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Δόνηση"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Επιλογή ήχου κλήσης"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Ορισμός προεπιλεγμένης υπενθύμισης"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Προεπιλεγμένος χρόνος υπενθύμισης"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Προεπιλεγμένος χρόνος υπενθύμισης"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 81165ac..7761958 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Calendario"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"¡Bienvenido a Google Calendar!"</font>" "\n" Un método de Google para administrar tu programación basado en la idea de que programar los eventos puede ser más intuitivo, eficaz y accesible."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"¡Bienvenido a Google Calendar!"</font>" "\n" Un método de Google para administrar tu programación basado en la idea de que programar los eventos puede ser más intuitivo, eficaz y accesible."</string>
<string name="what_label" msgid="1933578391985810049">"Qué"</string>
<string name="when_label" msgid="894636567327261599">"Cuándo"</string>
<string name="where_label" msgid="3472482206065595118">"Dónde"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Invitados"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Repetición"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Sin asunto)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Nombre del evento"</string>
<string name="hint_where" msgid="3116239630502213241">"Ubicación del evento"</string>
<string name="hint_description" msgid="4198202812240544553">"Descripción del evento"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"Direcciones de correo electrónico"</string>
<string name="creating_event" msgid="8237877638457604455">"Creando evento…"</string>
<string name="saving_event" msgid="8853425146353785688">"Guardando evento…"</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Cargando calendarios"</string>
@@ -78,23 +81,25 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Mostrar opciones adicionales"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Ocultar opciones adicionales"</string>
<string name="description_label" msgid="7193475046717505855">"Descripción"</string>
- <string name="presence_label" msgid="2155381988163502023">"Presencia"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Mostrarme como"</string>
<string name="privacy_label" msgid="9215031965259597335">"Privacidad"</string>
<string name="reminders_label" msgid="8345054160145333166">"Recordatorios"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"No hay calendarios"</string>
<string name="no_calendars_found" msgid="755379468136462058">"No tienes calendarios."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Calendario"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organizador"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Zona horaria local"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"¿Asistirás?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Hoy"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Hoy, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Cargando…"</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Mostrando eventos desde <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Presionar para buscar más."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Mostrando eventos hasta <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Presionar para buscar más."</string>
<string name="num_events" msgid="3351672964607162257">"Cant. de eventos"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Editar evento"</string>
<string name="delete_label" msgid="874742811918030739">"Suprimir"</string>
<string name="delete_event_label" msgid="3738436215987360463">"Suprimir evento"</string>
- <!-- no translation found for save_label (2133599104834432589) -->
- <skip />
- <!-- no translation found for discard_label (4510607554910139220) -->
- <skip />
+ <string name="save_label" msgid="2133599104834432589">"Finalizado"</string>
+ <string name="discard_label" msgid="4510607554910139220">"Revertir"</string>
<string name="import_label" msgid="6993796574741477222">"Importar"</string>
<string name="snooze_all_label" msgid="4994488813124409077">"Posponer todos"</string>
<string name="dismiss_all_label" msgid="1834400025868091301">"Rechazar todos"</string>
@@ -120,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Alertas y notificaciones"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Vibrar"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Seleccionar tono de llamada"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Config. recordatorio predet."</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Hora predeterminada de recordatorio"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Hora predeterminada de recordatorio"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 9d8ad24..2cc16ba 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Calendario"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Bienvenido a Google Calendar"</font>\n" Una propuesta de Google para gestionar su agenda basándonos en la idea de que la programación de los eventos puede ser más intuitiva, eficaz y accesible."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Te damos la bienvenida a Google Calendar,"</font>\n" un servicio de administración de calendario creado por Google para hacer que la programación de los eventos sea más intuitiva, eficaz y accesible."</string>
<string name="what_label" msgid="1933578391985810049">"Asunto"</string>
<string name="when_label" msgid="894636567327261599">"Cuándo"</string>
<string name="where_label" msgid="3472482206065595118">"Dónde"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Invitados"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Repetición"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Sin asunto)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Nombre del evento"</string>
<string name="hint_where" msgid="3116239630502213241">"Ubicación del evento"</string>
<string name="hint_description" msgid="4198202812240544553">"Descripción del evento"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"Direcciones de correo electrónico"</string>
<string name="creating_event" msgid="8237877638457604455">"Creando evento..."</string>
<string name="saving_event" msgid="8853425146353785688">"Guardando evento..."</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Cargando calendarios"</string>
@@ -78,15 +81,19 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Mostrar opciones extra"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Ocultar opciones extra"</string>
<string name="description_label" msgid="7193475046717505855">"Descripción"</string>
- <string name="presence_label" msgid="2155381988163502023">"Presencia"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Mostrarme como"</string>
<string name="privacy_label" msgid="9215031965259597335">"Privacidad"</string>
<string name="reminders_label" msgid="8345054160145333166">"Recordatorios"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"No hay calendarios."</string>
<string name="no_calendars_found" msgid="755379468136462058">"No tienes ningún calendario."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Calendario"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organizador"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Zona horaria local"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"¿Asistiendo?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Hoy"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Hoy, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Cargando..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Mostrando eventos a partir del <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Toca aquí para buscar más."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Mostrando eventos hasta el <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Toca aquí para buscar más."</string>
<string name="num_events" msgid="3351672964607162257">"Número de eventos"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Editar evento"</string>
<string name="delete_label" msgid="874742811918030739">"Eliminar"</string>
@@ -118,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Alertas y notificaciones"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Vibrar"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Seleccionar tono"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Recordatorio predeterminado"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Duración predeterminada del recordatorio"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Duración predeterminada del recordatorio"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index e4bf32f..400dbab 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Agenda"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Bienvenue dans Google Agenda !"</font>" "\n"La solution de Google pour gérer votre emploi du temps en planifiant vos événements de manière plus intuitive, efficace et directe."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Bienvenue dans Google Agenda !"</font>" "\n"La solution de Google pour gérer votre emploi du temps en planifiant vos événements de manière plus intuitive, efficace et directe."</string>
<string name="what_label" msgid="1933578391985810049">"Objet"</string>
<string name="when_label" msgid="894636567327261599">"Date"</string>
<string name="where_label" msgid="3472482206065595118">"Lieu"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Invités"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Fréquence"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Aucun objet)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Nom de l\'événement"</string>
<string name="hint_where" msgid="3116239630502213241">"Lieu de l\'événement"</string>
<string name="hint_description" msgid="4198202812240544553">"Description de l\'événement"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"Adresses e-mail"</string>
<string name="creating_event" msgid="8237877638457604455">"Création de l\'événement…"</string>
<string name="saving_event" msgid="8853425146353785688">"Enregistrement de l\'événement…"</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Chargement des agendas"</string>
@@ -78,15 +81,19 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Afficher les options supplémentaires"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Masquer options supplémentaires"</string>
<string name="description_label" msgid="7193475046717505855">"Description"</string>
- <string name="presence_label" msgid="2155381988163502023">"Présence"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Ma disponibilité"</string>
<string name="privacy_label" msgid="9215031965259597335">"Confidentialité"</string>
<string name="reminders_label" msgid="8345054160145333166">"Rappels"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Pas d\'agenda"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Vous n\'avez pas d\'agenda."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Agenda"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organisateur"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Fuseau horaire local"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Participation ?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Aujourd\'hui"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Aujourd\'hui, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Chargement…"</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Affichage des événements depuis <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Appuyez pour en consulter d\'autres."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Affichage des événements jusqu\'à <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Appuyez pour en consulter d\'autres."</string>
<string name="num_events" msgid="3351672964607162257">"Nbre d\'événements"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Modifier l\'événement"</string>
<string name="delete_label" msgid="874742811918030739">"Supprimer"</string>
@@ -118,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Alertes et notifications"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Vibreur"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Sélectionner la sonnerie"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Définir le rappel par défaut"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Rappel par défaut"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Rappel par défaut"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index d0ff1e5..12fd3e0 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Calendario"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Benvenuto in Google Calendar!"</font>" "\n"Un approccio di Google alla gestione della tua pianificazione in base all\'idea che gli eventi di pianificazione possano essere più intuitivi, efficaci e accessibili."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Benvenuto in Google Calendar!"</font>" "\n"Un approccio di Google alla gestione della tua pianificazione in base all\'idea che gli eventi di pianificazione possano essere più intuitivi, efficaci e accessibili."</string>
<string name="what_label" msgid="1933578391985810049">"Che cosa"</string>
<string name="when_label" msgid="894636567327261599">"Intervallo di tempo"</string>
<string name="where_label" msgid="3472482206065595118">"Dove"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Invitati"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Ripetizione"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Nessun oggetto)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Nome evento"</string>
<string name="hint_where" msgid="3116239630502213241">"Luogo evento"</string>
<string name="hint_description" msgid="4198202812240544553">"Descrizione evento"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"Indirizzi email"</string>
<string name="creating_event" msgid="8237877638457604455">"Creazione evento in corso…"</string>
<string name="saving_event" msgid="8853425146353785688">"Salvataggio evento in corso…"</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Caricamento calendari"</string>
@@ -78,15 +81,19 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Mostra opzioni aggiuntive"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Nascondi opzioni aggiuntive"</string>
<string name="description_label" msgid="7193475046717505855">"Descrizione"</string>
- <string name="presence_label" msgid="2155381988163502023">"Presenza"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Imposta il mio stato su"</string>
<string name="privacy_label" msgid="9215031965259597335">"Privacy"</string>
<string name="reminders_label" msgid="8345054160145333166">"Promemoria"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Nessun calendario"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Non sono presenti calendari."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Calendario"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organizzatore"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Fuso orario locale"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Partecipanti?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Oggi"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Oggi, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Caricamento..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Eventi visualizzati a partire da <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Tocca per cercarne altri."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Eventi visualizzati fino a <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Tocca per cercarne altri."</string>
<string name="num_events" msgid="3351672964607162257">"N. eventi"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Modifica evento"</string>
<string name="delete_label" msgid="874742811918030739">"Elimina"</string>
@@ -118,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Avvisi e notifiche"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Vibrazione"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Seleziona suoneria"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Promemoria predefinito"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Imposta promemoria predefinito"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Imposta promemoria predefinito"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index fb83479..311278c 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"カレンダー"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Googleカレンダーにようこそ"</font>" "\n"いつでもどこでも簡単にスケジュールを管理できます。"</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Googleカレンダーへようこそ!"</font>\n"スケジュールに簡単にアクセスして、より直感的、効率的に予定を管理できます。"</string>
<string name="what_label" msgid="1933578391985810049">"タイトル"</string>
<string name="when_label" msgid="894636567327261599">"日時"</string>
<string name="where_label" msgid="3472482206065595118">"場所"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"ゲスト"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g>(<xliff:g id="GUEST_COUNT">%d</xliff:g>名)"</string>
<string name="repeats_label" msgid="7414023871434593196">"繰り返し"</string>
<string name="no_title_label" msgid="7352118984125107858">"(無題)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"タイトル"</string>
<string name="hint_where" msgid="3116239630502213241">"場所"</string>
<string name="hint_description" msgid="4198202812240544553">"内容"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"メールアドレス"</string>
<string name="creating_event" msgid="8237877638457604455">"予定を作成中..."</string>
<string name="saving_event" msgid="8853425146353785688">"予定を保存中..."</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"カレンダーを読み込み中"</string>
@@ -78,15 +81,19 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"詳細項目を表示する"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"詳細項目を表示しない"</string>
<string name="description_label" msgid="7193475046717505855">"内容"</string>
- <string name="presence_label" msgid="2155381988163502023">"ステータス"</string>
+ <string name="presence_label" msgid="6169724148441175862">"外部向け表示"</string>
<string name="privacy_label" msgid="9215031965259597335">"公開設定"</string>
<string name="reminders_label" msgid="8345054160145333166">"通知"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"カレンダーがありません"</string>
<string name="no_calendars_found" msgid="755379468136462058">"カレンダーがありません。"</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"カレンダー"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"主催者"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"ローカルタイムゾーン"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"参加しますか?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"今日"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"今日: <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"読み込み中..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"<xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>以降の予定を表示しています。タップするとそれ以外の予定を表示します。"</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"<xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>までの予定を表示しています。タップするとそれ以外の予定を表示します。"</string>
<string name="num_events" msgid="3351672964607162257">"予定数"</string>
<string name="edit_event_label" msgid="2900418236819088363">"予定を編集"</string>
<string name="delete_label" msgid="874742811918030739">"削除"</string>
@@ -118,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"通知方法"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"バイブレーション"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"着信音を選択"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"通知設定"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"デフォルトの通知時間"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"リマインダー通知"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index ee37c46..783f5f2 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"캘린더"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Google 캘린더에 오신 것을 환영합니다."</font>" "\n" Google 캘린더는 더 간편하고 효율적이며 사용하기 쉬운 일정 관리 서비스를 제공하는 Google 서비스입니다."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Google 캘린더에 오신 것을 환영합니다."</font>" "\n"Google 캘린더는 더 간편하고 효율적이며 사용하기 쉬운 일정 관리 서비스를 제공하는 Google 서비스입니다."</string>
<string name="what_label" msgid="1933578391985810049">"내용"</string>
<string name="when_label" msgid="894636567327261599">"일시"</string>
<string name="where_label" msgid="3472482206065595118">"장소"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"참석자"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g>(<xliff:g id="GUEST_COUNT">%d</xliff:g>개)"</string>
<string name="repeats_label" msgid="7414023871434593196">"반복"</string>
<string name="no_title_label" msgid="7352118984125107858">"(제목 없음)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"일정 이름"</string>
<string name="hint_where" msgid="3116239630502213241">"일정 장소"</string>
<string name="hint_description" msgid="4198202812240544553">"일정 설명"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"이메일 주소"</string>
<string name="creating_event" msgid="8237877638457604455">"일정을 만드는 중..."</string>
<string name="saving_event" msgid="8853425146353785688">"일정 저장 중..."</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"캘린더 로드 중"</string>
@@ -78,23 +81,25 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"나머지 옵션 표시"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"나머지 옵션 숨기기"</string>
<string name="description_label" msgid="7193475046717505855">"설명"</string>
- <string name="presence_label" msgid="2155381988163502023">"상태"</string>
+ <string name="presence_label" msgid="6169724148441175862">"내 상태"</string>
<string name="privacy_label" msgid="9215031965259597335">"공개여부"</string>
<string name="reminders_label" msgid="8345054160145333166">"알림"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"캘린더 없음"</string>
<string name="no_calendars_found" msgid="755379468136462058">"캘린더가 없습니다."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"캘린더"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"주최자"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"현지 시간대"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"참석하시겠습니까?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"오늘"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"오늘, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"로드 중..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"<xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g> 이후의 일정을 표시 중입니다. 더 보려면 누르세요."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"<xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>까지의 일정을 표시 중입니다. 더 보려면 누르세요."</string>
<string name="num_events" msgid="3351672964607162257">"일정 개수"</string>
<string name="edit_event_label" msgid="2900418236819088363">"일정 수정"</string>
<string name="delete_label" msgid="874742811918030739">"삭제"</string>
<string name="delete_event_label" msgid="3738436215987360463">"일정 삭제"</string>
- <!-- no translation found for save_label (2133599104834432589) -->
- <skip />
- <!-- no translation found for discard_label (4510607554910139220) -->
- <skip />
+ <string name="save_label" msgid="2133599104834432589">"완료"</string>
+ <string name="discard_label" msgid="4510607554910139220">"되돌리기"</string>
<string name="import_label" msgid="6993796574741477222">"가져오기"</string>
<string name="snooze_all_label" msgid="4994488813124409077">"모든 알람 일시 중지"</string>
<string name="dismiss_all_label" msgid="1834400025868091301">"모두 해제"</string>
@@ -120,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"알리미와 알림"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"진동"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"알림음 선택"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"기본 알림 설정"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"기본 알림 시간"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"기본 알림 시간"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-nb/arrays.xml b/res/values-nb/arrays.xml
index 60e0341..3f099c6 100644
--- a/res/values-nb/arrays.xml
+++ b/res/values-nb/arrays.xml
@@ -89,6 +89,8 @@
<item msgid="1341910309321756150">"Denne og framtidige hendelser"</item>
<item msgid="6315440193748139854">"Alle hendelser"</item>
</string-array>
- <!-- no translation found for change_response_labels:0 (2012815396992847028) -->
- <!-- no translation found for change_response_labels:1 (140670561403965380) -->
+ <string-array name="change_response_labels">
+ <item msgid="2012815396992847028">"Bare denne forekomsten"</item>
+ <item msgid="140670561403965380">"Alle hendelser i serien"</item>
+ </string-array>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 1d45a7f..4e5641b 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Kalender"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Velkommen til Google-kalenderen!"</font>" "\n"En Google-løsning for å vedlikeholde timeplanen din, bygget på ideen om at å planlegge ting skal være intuitivt, effektivt og tilgjengelig."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Velkommen til Google-kalenderen!"</font>" "\n"En Google-løsning for å vedlikeholde timeplanen din, bygget på ideen om at å planlegge ting skal være intuitivt, effektivt og tilgjengelig."</string>
<string name="what_label" msgid="1933578391985810049">"Hva"</string>
<string name="when_label" msgid="894636567327261599">"Når"</string>
<string name="where_label" msgid="3472482206065595118">"Hvor"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Gjester"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Gjentas"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Mangler emne)"</string>
<plurals name="Nminutes">
@@ -44,10 +46,10 @@
<string name="day_view" msgid="4467754504843584437">"Dag"</string>
<string name="week_view" msgid="6775043022159508060">"Uke"</string>
<string name="month_view" msgid="7287101431749285412">"Måned"</string>
- <string name="event_view" msgid="7621353079385122317">"Vis hendelse"</string>
- <string name="event_create" msgid="6941040556709110617">"Ny hendelse"</string>
- <string name="event_edit" msgid="4006961928830064662">"Rediger hendelse"</string>
- <string name="event_delete" msgid="2386667193277770442">"Slett hendelse"</string>
+ <string name="event_view" msgid="7621353079385122317">"Vis aktivitet"</string>
+ <string name="event_create" msgid="6941040556709110617">"Ny aktivitet"</string>
+ <string name="event_edit" msgid="4006961928830064662">"Rediger aktivitet"</string>
+ <string name="event_delete" msgid="2386667193277770442">"Slett aktivitet"</string>
<string name="goto_today" msgid="9162136434410891230">"I dag"</string>
<string name="menu_select_calendars" msgid="6610470242968973936">"Mine kalendre"</string>
<string name="menu_preferences" msgid="9120238479503233925">"Innstillinger"</string>
@@ -56,19 +58,20 @@
<string name="add_calendars" msgid="1013326491414656517">"Legg til kalendre"</string>
<string name="remove_calendars" msgid="1395151808187126222">"Fjern kalendre"</string>
<string name="event_edit_title" msgid="8487120407086532444">"Hendelsesdetaljer"</string>
- <string name="hint_what" msgid="709155115005044531">"Navn på hendelse"</string>
+ <string name="hint_what" msgid="709155115005044531">"Navn på aktivitet"</string>
<string name="hint_where" msgid="3116239630502213241">"Sted"</string>
- <string name="hint_description" msgid="4198202812240544553">"Beskrivelse av hendelsen"</string>
- <string name="creating_event" msgid="8237877638457604455">"Lager hendelse…"</string>
- <string name="saving_event" msgid="8853425146353785688">"Lagrer hendelse…"</string>
+ <string name="hint_description" msgid="4198202812240544553">"Beskrivelse av aktivitet"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"E-postadresser"</string>
+ <string name="creating_event" msgid="8237877638457604455">"Lager aktivitet…"</string>
+ <string name="saving_event" msgid="8853425146353785688">"Lagrer aktivitet…"</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Laster kalendre"</string>
<string name="loading_calendars_message" msgid="8266077515011438420">"Laster kalendre…"</string>
<string name="alert_title" msgid="7869592820853066853">"Kalendervarslinger"</string>
<string name="alert_when_label" msgid="106946701143025116">"Hva:"</string>
<string name="alert_where_label" msgid="4255965543493921820">"Hvor:"</string>
- <string name="alert_missed_events_single" msgid="8042891195314876684">"(<xliff:g id="REMINDER_COUNT">%s</xliff:g> hendelse til)"</string>
- <string name="alert_missed_events_multiple" msgid="8499734506511653919">"(<xliff:g id="REMINDER_COUNT">%s</xliff:g> hendelser til)"</string>
- <string name="event_info_title" msgid="56024222340421620">"Vis hendelse"</string>
+ <string name="alert_missed_events_single" msgid="8042891195314876684">"(<xliff:g id="REMINDER_COUNT">%s</xliff:g> aktiviteter til)"</string>
+ <string name="alert_missed_events_multiple" msgid="8499734506511653919">"(<xliff:g id="REMINDER_COUNT">%s</xliff:g> aktiviteter til)"</string>
+ <string name="event_info_title" msgid="56024222340421620">"Vis aktivitet"</string>
<string name="event_info_title_invite" msgid="9160326363769403865">"Møteinvitasjon"</string>
<string name="add_new_reminder" msgid="4171314793889890481">"Legg til påminnelse"</string>
<string name="edit_event_to_label" msgid="795012981218062421">"Til"</string>
@@ -78,27 +81,29 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Vis ekstra valg"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Skjul ekstra valg"</string>
<string name="description_label" msgid="7193475046717505855">"Beskrivelse"</string>
- <string name="presence_label" msgid="2155381988163502023">"Vis meg som"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Vis meg som"</string>
<string name="privacy_label" msgid="9215031965259597335">"Personvern"</string>
<string name="reminders_label" msgid="8345054160145333166">"Påminnelser"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Ingen kalendre"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Du har ingen kalendre."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Kalender"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Arrangør"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Lokal tidssone"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Kommer du?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"I dag"</string>
- <string name="num_events" msgid="3351672964607162257">"Ant. hendelser"</string>
- <string name="edit_event_label" msgid="2900418236819088363">"Rediger hendelse"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"I dag, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Laster inn ..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Viser aktiviteter siden <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Trykk for å se etter flere."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Viser aktiviteter inntil <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Trykk for å se etter flere."</string>
+ <string name="num_events" msgid="3351672964607162257">"Ant. aktiviteter"</string>
+ <string name="edit_event_label" msgid="2900418236819088363">"Rediger aktivitet"</string>
<string name="delete_label" msgid="874742811918030739">"Slett"</string>
- <string name="delete_event_label" msgid="3738436215987360463">"Slett hendelse"</string>
- <!-- no translation found for save_label (2133599104834432589) -->
- <skip />
- <!-- no translation found for discard_label (4510607554910139220) -->
- <skip />
- <string name="import_label" msgid="6993796574741477222">"Importer"</string>
+ <string name="delete_event_label" msgid="3738436215987360463">"Slett aktivitet"</string>
+ <string name="save_label" msgid="2133599104834432589">"OK"</string>
+ <string name="discard_label" msgid="4510607554910139220">"Tilbakestill"</string>
+ <string name="import_label" msgid="6993796574741477222">"Importér"</string>
<string name="snooze_all_label" msgid="4994488813124409077">"Slumre alle"</string>
<string name="dismiss_all_label" msgid="1834400025868091301">"Skjul alle"</string>
- <string name="does_not_repeat" msgid="1877681921029738999">"Engangshendelse"</string>
+ <string name="does_not_repeat" msgid="1877681921029738999">"Engangsaktivitet"</string>
<string name="daily" msgid="560350706808680448">"Daglig"</string>
<string name="every_weekday" msgid="5896574838353902978">"Hverdager (man–fre)"</string>
<string name="weekly" msgid="2006107373119027114">"Ukentlig (hver <xliff:g id="DAYS_OF_WEEK">%s</xliff:g>)"</string>
@@ -106,21 +111,21 @@
<string name="monthly_on_day" msgid="5370155265278821156">"Hver måned (den <xliff:g id="DAY_OF_MONTH">%s</xliff:g>.)"</string>
<string name="yearly" msgid="8576208380302949002">"Hvert år (<xliff:g id="DATES">%s</xliff:g>)"</string>
<string name="custom" msgid="3015205670911301856">"Egendefinert… (kan ikke settes på telefonen)"</string>
- <string name="modify_event" msgid="4501745557655992422">"Endre bare denne hendelsen."</string>
- <string name="modify_all" msgid="7506579040966638538">"Endre alle hendelser i denne serien."</string>
- <string name="modify_all_following" msgid="4333745734894639276">"Endre denne og alle framtidige hendelser."</string>
- <string name="delete_this_event_title" msgid="8738491083082780492">"Denne hendelsen vil bli slettet."</string>
+ <string name="modify_event" msgid="4501745557655992422">"Endre bare denne aktiviteten."</string>
+ <string name="modify_all" msgid="7506579040966638538">"Endre alle aktiviteter i denne serien."</string>
+ <string name="modify_all_following" msgid="4333745734894639276">"Endre denne og alle framtidige aktiviteter."</string>
+ <string name="delete_this_event_title" msgid="8738491083082780492">"Denne aktiviteten vil bli slettet."</string>
<string name="delete_title" msgid="5143743507524995383">"Slett"</string>
<string name="change_response_title" msgid="7704714776070974984">"Endre svar"</string>
<string name="preferences_title" msgid="8045855493262131773">"Innstillinger"</string>
<string name="preferences_general_title" msgid="2069075513760573117">"Kalender visningsinnstilling"</string>
<string name="preferences_alerts_title" msgid="6511227171114210908">"Innstillinger for påminnelser"</string>
- <string name="preferences_hide_declined_title" msgid="1849295943669890034">"Skjul avslåtte hendelser"</string>
+ <string name="preferences_hide_declined_title" msgid="1849295943669890034">"Skjul avslåtte aktiviteter"</string>
<string name="preferences_alerts_type_title" msgid="6934441966757833908">"Sett varsling og påminnelser"</string>
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Varsling og påminnelser"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Vibrer"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Velg ringetone"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Sett standard påminnelse"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Sett standard påminnelse"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Standard påminnelsestid"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index cea5a51..f482e1d 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Agenda"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Welkom bij Google Agenda!"</font>" "\n" Een Google-benadering om uw planning te beheren, gebaseerd op het idee dat het plannen van afspraken intuïtiever, efficiënter en toegankelijker kan."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Welkom bij Google Agenda!"</font>" "\n"Een Google-benadering om uw planning te beheren, gebaseerd op het idee dat het plannen van afspraken intuïtiever, efficiënter en toegankelijker kan."</string>
<string name="what_label" msgid="1933578391985810049">"Wat"</string>
<string name="when_label" msgid="894636567327261599">"Wanneer"</string>
<string name="where_label" msgid="3472482206065595118">"Waar"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Gasten"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Herhaling"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Geen onderwerp)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Afspraaknaam"</string>
<string name="hint_where" msgid="3116239630502213241">"Locatie van de afspraak"</string>
<string name="hint_description" msgid="4198202812240544553">"Beschrijving van afspraak"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"E-mailadressen"</string>
<string name="creating_event" msgid="8237877638457604455">"Afspraak wordt gemaakt…"</string>
<string name="saving_event" msgid="8853425146353785688">"Afspraak wordt opgeslagen..."</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Agenda\'s worden geladen"</string>
@@ -78,15 +81,19 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Extra opties weergeven"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Extra opties verbergen"</string>
<string name="description_label" msgid="7193475046717505855">"Beschrijving"</string>
- <string name="presence_label" msgid="2155381988163502023">"Beschikbaarheid"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Mij weergeven als"</string>
<string name="privacy_label" msgid="9215031965259597335">"Privacy"</string>
<string name="reminders_label" msgid="8345054160145333166">"Herinneringen"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Geen agenda\'s"</string>
<string name="no_calendars_found" msgid="755379468136462058">"U heeft geen agenda\'s."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Agenda"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organisator"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Lokale tijdzone"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Aanwezig?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Vandaag"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Vandaag, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Laden..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Afspraken weergeven sinds <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Tik om er meer te zoeken."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Afspraken weergeven tot <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Tik om er meer te zoeken."</string>
<string name="num_events" msgid="3351672964607162257">"Aantal afspraken"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Afspraak bewerken"</string>
<string name="delete_label" msgid="874742811918030739">"Verwijderen"</string>
@@ -118,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Meldingen en berichten"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Trillen"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Beltoon selecteren"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Standaardherinnering instellen"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Standaard herinneringstijd"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Standaard herinneringstijd"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 9f89049..49f844e 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Kalendarz"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Witamy w Kalendarzu Google!"</font>" "\n"Intuicyjne, wydajne i dostępne planowanie wydarzeń w celu zarządzania terminami według Google."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Witamy w Kalendarzu Google!"</font>" "\n"Zarządzanie harmonogramem w stylu Google, dzięki któremu planowanie wydarzeń jest intuicyjne, wydajne i wygodne."</string>
<string name="what_label" msgid="1933578391985810049">"Co"</string>
<string name="when_label" msgid="894636567327261599">"Kiedy"</string>
<string name="where_label" msgid="3472482206065595118">"Gdzie"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Goście"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Powtórzenie"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Brak tematu)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Nazwa wydarzenia"</string>
<string name="hint_where" msgid="3116239630502213241">"Lokalizacja wydarzenia"</string>
<string name="hint_description" msgid="4198202812240544553">"Opis wydarzenia"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"Adresy e-mail"</string>
<string name="creating_event" msgid="8237877638457604455">"Tworzenie wydarzenia..."</string>
<string name="saving_event" msgid="8853425146353785688">"Zapisywanie wydarzenia..."</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Ładowanie kalendarzy"</string>
@@ -78,15 +81,19 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Pokaż dodatkowe opcje"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Ukryj dodatkowe opcje"</string>
<string name="description_label" msgid="7193475046717505855">"Opis"</string>
- <string name="presence_label" msgid="2155381988163502023">"Obecność"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Pokaż mój stan jako"</string>
<string name="privacy_label" msgid="9215031965259597335">"Prywatność"</string>
<string name="reminders_label" msgid="8345054160145333166">"Przypomnienia"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Brak kalendarzy"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Nie masz kalendarzy."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Kalendarz"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organizator"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Lokalna strefa czasowa"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Uczestnictwo"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Dzisiaj"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Dzisiaj, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Trwa wczytywanie..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Pokazywanie wydarzeń od dnia <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Dotknij, aby wyszukać więcej."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Pokazywanie wydarzeń do dnia <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Dotknij, aby wyszukać więcej."</string>
<string name="num_events" msgid="3351672964607162257">"Liczba wydarzeń"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Edytuj wydarzenie"</string>
<string name="delete_label" msgid="874742811918030739">"Usuń"</string>
@@ -118,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Alerty i powiadomienia"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Wibracje"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Wybierz dzwonek"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Domyślne przypomnienie"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Domyślna godzina przypomnienia"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Domyślne przypomnienie"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 87152c5..05d3e24 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Calendário"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Bem-vindo ao Calendário do Google!"</font>" "\n"Uma abordagem da Google para gerir a sua agenda, baseada na ideia de que agendar eventos pode ser mais intuitivo, eficiente e acessível."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Bem-vindo ao Calendário Google!"</font>" "\n"Uma abordagem da Google para gerir a sua agenda, com base na ideia de que agendar eventos pode ser mais intuitivo, eficiente e acessível."</string>
<string name="what_label" msgid="1933578391985810049">"O quê"</string>
<string name="when_label" msgid="894636567327261599">"Quando"</string>
<string name="where_label" msgid="3472482206065595118">"Onde"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Convidados"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Repetição"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Sem assunto)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Nome do evento"</string>
<string name="hint_where" msgid="3116239630502213241">"Localização do evento"</string>
<string name="hint_description" msgid="4198202812240544553">"Descrição do evento"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"Endereços de e-mail"</string>
<string name="creating_event" msgid="8237877638457604455">"A criar evento..."</string>
<string name="saving_event" msgid="8853425146353785688">"A guardar evento..."</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"A carregar calendários"</string>
@@ -78,23 +81,25 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Mostrar opções extra"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Ocultar opções extra"</string>
<string name="description_label" msgid="7193475046717505855">"Descrição"</string>
- <string name="presence_label" msgid="2155381988163502023">"Presença"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Mostrar-me como"</string>
<string name="privacy_label" msgid="9215031965259597335">"Privacidade"</string>
<string name="reminders_label" msgid="8345054160145333166">"Lembretes"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Sem calendários"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Não tem calendários."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Calendário"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organizador"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Fuso horário local"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Assistir?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Hoje"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Hoje, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"A carregar..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"A mostrar eventos desde <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Toque para procurar mais."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"A mostrar eventos até <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Toque para procurar mais."</string>
<string name="num_events" msgid="3351672964607162257">"N.º de eventos"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Editar evento"</string>
<string name="delete_label" msgid="874742811918030739">"Eliminar"</string>
<string name="delete_event_label" msgid="3738436215987360463">"Eliminar evento"</string>
- <!-- no translation found for save_label (2133599104834432589) -->
- <skip />
- <!-- no translation found for discard_label (4510607554910139220) -->
- <skip />
+ <string name="save_label" msgid="2133599104834432589">"Concluído"</string>
+ <string name="discard_label" msgid="4510607554910139220">"Reverter"</string>
<string name="import_label" msgid="6993796574741477222">"Importar"</string>
<string name="snooze_all_label" msgid="4994488813124409077">"Suspender todos"</string>
<string name="dismiss_all_label" msgid="1834400025868091301">"Cancelar tudo"</string>
@@ -120,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Alertas e notificações"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Vibrar"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Seleccionar toque"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Definir lembrete predefinido"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Hora predefinida do lembrete"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Hora predefinida do lembrete"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-pt/arrays.xml b/res/values-pt/arrays.xml
index de42eb3..362c642 100644
--- a/res/values-pt/arrays.xml
+++ b/res/values-pt/arrays.xml
@@ -90,7 +90,7 @@
<item msgid="6315440193748139854">"Todos os eventos"</item>
</string-array>
<string-array name="change_response_labels">
- <item msgid="2012815396992847028">"Somente nessa ocasião"</item>
+ <item msgid="2012815396992847028">"Somente este evento"</item>
<item msgid="140670561403965380">"Todos os eventos da série"</item>
</string-array>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index e534750..4e5430b 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Agenda"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Bem-vindo ao Google Agenda!"</font>" "\n" Uma abordagem do Google para organizar seus horários baseada na idéia de que eventos programados podem ser mais intuitivos, eficientes e acessíveis."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Bem-vindo ao Google Agenda!"</font>" "\n" Uma abordagem do Google para organizar seus horários com base na ideia de que programar eventos pode ser mais intuitivo, eficiente e acessível."</string>
<string name="what_label" msgid="1933578391985810049">"O que"</string>
<string name="when_label" msgid="894636567327261599">"Quando"</string>
<string name="where_label" msgid="3472482206065595118">"Onde"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Convidados"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Repetições"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Sem título)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Nome do evento"</string>
<string name="hint_where" msgid="3116239630502213241">"Local do evento"</string>
<string name="hint_description" msgid="4198202812240544553">"Descrição do evento"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"Endereços de e-mail"</string>
<string name="creating_event" msgid="8237877638457604455">"Criando um evento…"</string>
<string name="saving_event" msgid="8853425146353785688">"Salvando evento…"</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Carregando as agendas"</string>
@@ -78,23 +81,25 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Mostrar opções extras"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Ocultar opções extras"</string>
<string name="description_label" msgid="7193475046717505855">"Descrição"</string>
- <string name="presence_label" msgid="2155381988163502023">"Presença"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Mostrar-me como"</string>
<string name="privacy_label" msgid="9215031965259597335">"Privacidade"</string>
<string name="reminders_label" msgid="8345054160145333166">"Lembretes"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Nenhuma agenda disponível"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Você não tem nenhuma agenda."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Agenda"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organizador"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Localizar fuso horário"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Você participará?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Hoje"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Hoje, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Carregando..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Mostrando eventos desde <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Toque para procurar mais."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Mostrando eventos até <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Toque para procurar mais."</string>
<string name="num_events" msgid="3351672964607162257">"Núm. eventos"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Editar evento"</string>
<string name="delete_label" msgid="874742811918030739">"Excluir"</string>
<string name="delete_event_label" msgid="3738436215987360463">"Excluir evento"</string>
- <!-- no translation found for save_label (2133599104834432589) -->
- <skip />
- <!-- no translation found for discard_label (4510607554910139220) -->
- <skip />
+ <string name="save_label" msgid="2133599104834432589">"Concluído"</string>
+ <string name="discard_label" msgid="4510607554910139220">"Reverter"</string>
<string name="import_label" msgid="6993796574741477222">"Importar"</string>
<string name="snooze_all_label" msgid="4994488813124409077">"Colocar todos em modo de espera"</string>
<string name="dismiss_all_label" msgid="1834400025868091301">"Encerrar tudo"</string>
@@ -115,12 +120,12 @@
<string name="preferences_title" msgid="8045855493262131773">"Configurações"</string>
<string name="preferences_general_title" msgid="2069075513760573117">"Configuração de visualização da agenda"</string>
<string name="preferences_alerts_title" msgid="6511227171114210908">"Configurações dos lembretes"</string>
- <string name="preferences_hide_declined_title" msgid="1849295943669890034">"Ocultar eventos recusados"</string>
+ <string name="preferences_hide_declined_title" msgid="1849295943669890034">"Ocultar recusados"</string>
<string name="preferences_alerts_type_title" msgid="6934441966757833908">"Definir alertas e notificações"</string>
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Alertas e notificações"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Vibrar"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Selecionar toque"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Definir lembrete padrão"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Tempo padrão do lembrete"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Momento padrão do lembrete"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 4e4f197..9eaaf3b 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Календарь"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Добро пожаловать в Календарь Google!"</font>" "\n"Подход Google к управлению расписанием основан на принципах интуитивности, эффективности и доступности."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Добро пожаловать в Календарь Google!"</font>" "\n"Подход Google к управлению расписанием основан на принципах интуитивности, эффективности и доступности."</string>
<string name="what_label" msgid="1933578391985810049">"Что"</string>
<string name="when_label" msgid="894636567327261599">"Когда"</string>
<string name="where_label" msgid="3472482206065595118">"Где"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Гости"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Повторять"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Без темы)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Название мероприятия"</string>
<string name="hint_where" msgid="3116239630502213241">"Место мероприятия"</string>
<string name="hint_description" msgid="4198202812240544553">"Описание мероприятия"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"Адреса электронной почты"</string>
<string name="creating_event" msgid="8237877638457604455">"Создание мероприятия…"</string>
<string name="saving_event" msgid="8853425146353785688">"Сохранение мероприятия…"</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Загрузка календарей"</string>
@@ -78,23 +81,25 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Показать дополнительные параметры"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Скрыть дополнительные параметры"</string>
<string name="description_label" msgid="7193475046717505855">"Описание"</string>
- <string name="presence_label" msgid="2155381988163502023">"Присутствие"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Мой статус:"</string>
<string name="privacy_label" msgid="9215031965259597335">"Конфиденциальность"</string>
<string name="reminders_label" msgid="8345054160145333166">"Напоминания"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Нет календарей"</string>
<string name="no_calendars_found" msgid="755379468136462058">"У вас нет календарей."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Календарь"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Организатор"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Часовой пояс"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Принять приглашение?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Сегодня"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Сегодня, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Загрузка…"</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Мероприятия с <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Нажмите для поиска других мероприятий."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Мероприятия до <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Нажмите для поиска других мероприятий."</string>
<string name="num_events" msgid="3351672964607162257">"Кол-во мероприятий"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Изменить мероприятие"</string>
<string name="delete_label" msgid="874742811918030739">"Удалить"</string>
<string name="delete_event_label" msgid="3738436215987360463">"Удалить мероприятие"</string>
- <!-- no translation found for save_label (2133599104834432589) -->
- <skip />
- <!-- no translation found for discard_label (4510607554910139220) -->
- <skip />
+ <string name="save_label" msgid="2133599104834432589">"Готово"</string>
+ <string name="discard_label" msgid="4510607554910139220">"Отменить изменения"</string>
<string name="import_label" msgid="6993796574741477222">"Импорт"</string>
<string name="snooze_all_label" msgid="4994488813124409077">"Отложить все"</string>
<string name="dismiss_all_label" msgid="1834400025868091301">"Отключить все"</string>
@@ -120,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Оповещения и уведомления"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Вибрация"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Выбрать мелодию"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Время напоминания по умолчанию"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Время напоминания по умолчанию"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Время напоминания по умолчанию"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index bf726b0..ee9e563 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Kalender"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Välkommen till Google Kalender!"</font>" "\n"Med Googles verktyg för schemaläggning sköter du planeringen på ett enkelt, effektivt och lättillgängligt sätt."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Välkommen till Google Kalender!"</font>" "\n"Med Googles verktyg för schemaläggning sköter du planeringen på ett enkelt, effektivt och lättillgängligt sätt."</string>
<string name="what_label" msgid="1933578391985810049">"Vad"</string>
<string name="when_label" msgid="894636567327261599">"När"</string>
<string name="where_label" msgid="3472482206065595118">"Var"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Gäster"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Upprepning"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Inget ämne)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Händelsens namn"</string>
<string name="hint_where" msgid="3116239630502213241">"Plats för händelsen"</string>
<string name="hint_description" msgid="4198202812240544553">"Beskrivning av händelse"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"E-postadresser"</string>
<string name="creating_event" msgid="8237877638457604455">"Skapar händelse…"</string>
<string name="saving_event" msgid="8853425146353785688">"Sparar händelse…"</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Läser in kalendrar"</string>
@@ -78,23 +81,25 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Visa fler alternativ"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Dölj fler alternativ"</string>
<string name="description_label" msgid="7193475046717505855">"Beskrivning"</string>
- <string name="presence_label" msgid="2155381988163502023">"Närvaro"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Visa mig som"</string>
<string name="privacy_label" msgid="9215031965259597335">"Sekretess"</string>
<string name="reminders_label" msgid="8345054160145333166">"Påminnelser"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Inga kalendrar"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Du har inga kalendrar."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Kalender"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Organisatör"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Lokal tidszon"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Delta?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Idag"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Idag, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Hämtar…"</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Visar händelser sedan <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>. Peka om du vill visa fler."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Visar händelser fram till <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>. Peka om du vill visa fler."</string>
<string name="num_events" msgid="3351672964607162257">"Antal händelser"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Redigera händelse"</string>
<string name="delete_label" msgid="874742811918030739">"Ta bort"</string>
<string name="delete_event_label" msgid="3738436215987360463">"Ta bort händelse"</string>
- <!-- no translation found for save_label (2133599104834432589) -->
- <skip />
- <!-- no translation found for discard_label (4510607554910139220) -->
- <skip />
+ <string name="save_label" msgid="2133599104834432589">"Färdig"</string>
+ <string name="discard_label" msgid="4510607554910139220">"Återgå"</string>
<string name="import_label" msgid="6993796574741477222">"Importera"</string>
<string name="snooze_all_label" msgid="4994488813124409077">"Sätt alla på snooze"</string>
<string name="dismiss_all_label" msgid="1834400025868091301">"Ta bort alla permanent"</string>
@@ -120,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Varningar och aviseringar"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Vibrera"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Välj ringsignal"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Ange standardtid för påmin."</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Standardtid för påminnelse"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Standardtid för påminnelse"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 9ce5809..860ec8a 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Takvim"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"Google Takvim\'e hoş geldiniz!"</font>" "\n"Programlı etkinliklerin daha fark edilir, daha etkin ve daha erişilebilir olabilmesi fikri üzerine inşa edilen, programınızı yönetmeniz için geliştirilmiş bir Google yaklaşımıdır."</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"Google Takvim\'e hoş geldiniz!"</font>" "\n"Programlı etkinliklerin daha fark edilir, daha etkin ve daha erişilebilir olabilmesi fikri üzerine inşa edilen, programınızı yönetmeniz için geliştirilmiş bir Google ürünüdür."</string>
<string name="what_label" msgid="1933578391985810049">"Başlık:"</string>
<string name="when_label" msgid="894636567327261599">"Zaman:"</string>
<string name="where_label" msgid="3472482206065595118">"Yer"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"Misafirler"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"Tekrar"</string>
<string name="no_title_label" msgid="7352118984125107858">"(Konu yok)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"Etkinlik adı"</string>
<string name="hint_where" msgid="3116239630502213241">"Etkinlik konumu"</string>
<string name="hint_description" msgid="4198202812240544553">"Etkinlik açıklaması"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"E-posta adresleri"</string>
<string name="creating_event" msgid="8237877638457604455">"Etkinlik oluşturuluyor..."</string>
<string name="saving_event" msgid="8853425146353785688">"Etkinlik kaydediliyor..."</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"Takvimler yükleniyor"</string>
@@ -78,23 +81,25 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"Ek seçenekleri göster"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"Ek seçenekleri gizle"</string>
<string name="description_label" msgid="7193475046717505855">"Açıklama"</string>
- <string name="presence_label" msgid="2155381988163502023">"Mevcudiyet"</string>
+ <string name="presence_label" msgid="6169724148441175862">"Durumumu şu şekilde göster:"</string>
<string name="privacy_label" msgid="9215031965259597335">"Gizlilik"</string>
<string name="reminders_label" msgid="8345054160145333166">"Hatırlatıcılar"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"Takvim yok"</string>
<string name="no_calendars_found" msgid="755379468136462058">"Hiç takviminiz yok."</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"Takvim"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"Düzenleyici"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"Yerel saat dilimi"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"Katılıyor musunuz?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"Bugün"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"Bugün, <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="loading" msgid="3772533493214331230">"Yükleniyor…"</string>
+ <string name="show_older_events" msgid="4031960000704544741">"Etkinlikler <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g> tarihinden itibaren gösteriliyor. Daha fazlasını görmek için hafifçe vurun."</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"Etkinlikler <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g> tarihine kadar gösteriliyor. Daha fazlasını görmek için hafifçe vurun."</string>
<string name="num_events" msgid="3351672964607162257">"Etkinlik sayısı"</string>
<string name="edit_event_label" msgid="2900418236819088363">"Etkinliği düzenle"</string>
<string name="delete_label" msgid="874742811918030739">"Sil"</string>
<string name="delete_event_label" msgid="3738436215987360463">"Etkinliği sil"</string>
- <!-- no translation found for save_label (2133599104834432589) -->
- <skip />
- <!-- no translation found for discard_label (4510607554910139220) -->
- <skip />
+ <string name="save_label" msgid="2133599104834432589">"Bitti"</string>
+ <string name="discard_label" msgid="4510607554910139220">"Geri Döndür"</string>
<string name="import_label" msgid="6993796574741477222">"İçe aktar"</string>
<string name="snooze_all_label" msgid="4994488813124409077">"Tümünü ertele"</string>
<string name="dismiss_all_label" msgid="1834400025868091301">"Tümünü kapat"</string>
@@ -120,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"Uyarılar ve bildirimler"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"Titret"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"Zil sesini seç"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"Varsayılan hatırlatıcıyı ayarla"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"Varsayılan hatırlatıcı süresi"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"Varsayılan hatırlatıcı süresi"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-zh-rCN/arrays.xml b/res/values-zh-rCN/arrays.xml
index 32877b2..7152bcd 100644
--- a/res/values-zh-rCN/arrays.xml
+++ b/res/values-zh-rCN/arrays.xml
@@ -51,7 +51,7 @@
<string-array name="preferences_alert_type_labels">
<item msgid="2724242028978664229">"警报"</item>
<item msgid="8723674378160518790">"状态栏通知"</item>
- <item msgid="8433736231022186661">"关"</item>
+ <item msgid="8433736231022186661">"关闭"</item>
</string-array>
<string-array name="availability">
<item msgid="454869065893453189">"忙碌"</item>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 7d1f12d..9574880 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"日历"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"欢迎使用 Google 日历!"</font>" "\n"Google 日历可用来管理您的日程计划,让您更直观、更高效、更轻松地安排活动。"</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"欢迎使用 Google 日历!"</font>" "\n"Google 日历可用来管理您的日程安排,让您更直观、更高效、更轻松地安排活动。"</string>
<string name="what_label" msgid="1933578391985810049">"内容"</string>
<string name="when_label" msgid="894636567327261599">"时间"</string>
<string name="where_label" msgid="3472482206065595118">"地点"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"来宾"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"重复"</string>
<string name="no_title_label" msgid="7352118984125107858">"(无主题)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"活动名称"</string>
<string name="hint_where" msgid="3116239630502213241">"活动地点"</string>
<string name="hint_description" msgid="4198202812240544553">"活动说明"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"电子邮件地址"</string>
<string name="creating_event" msgid="8237877638457604455">"正在创建活动..."</string>
<string name="saving_event" msgid="8853425146353785688">"正在保存活动..."</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"正在载入日历"</string>
@@ -78,23 +81,25 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"显示额外选项"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"隐藏额外选项"</string>
<string name="description_label" msgid="7193475046717505855">"说明"</string>
- <string name="presence_label" msgid="2155381988163502023">"状态"</string>
+ <string name="presence_label" msgid="6169724148441175862">"状态显示为"</string>
<string name="privacy_label" msgid="9215031965259597335">"隐私"</string>
<string name="reminders_label" msgid="8345054160145333166">"提醒"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"无日历"</string>
<string name="no_calendars_found" msgid="755379468136462058">"您没有日历。"</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"日历帐户"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"组织者"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"本地时区"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"参加吗?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"今天"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"今天(<xliff:g id="DATE">%1$s</xliff:g>)"</string>
+ <string name="loading" msgid="3772533493214331230">"正在载入..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"显示自 <xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>以来发生的活动。点按可查看更多活动。"</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"显示 <xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>之前发生的活动。点按可查看更多活动。"</string>
<string name="num_events" msgid="3351672964607162257">"活动数"</string>
<string name="edit_event_label" msgid="2900418236819088363">"编辑活动"</string>
<string name="delete_label" msgid="874742811918030739">"删除"</string>
<string name="delete_event_label" msgid="3738436215987360463">"删除活动"</string>
- <!-- no translation found for save_label (2133599104834432589) -->
- <skip />
- <!-- no translation found for discard_label (4510607554910139220) -->
- <skip />
+ <string name="save_label" msgid="2133599104834432589">"完成"</string>
+ <string name="discard_label" msgid="4510607554910139220">"还原"</string>
<string name="import_label" msgid="6993796574741477222">"导入"</string>
<string name="snooze_all_label" msgid="4994488813124409077">"全部暂停"</string>
<string name="dismiss_all_label" msgid="1834400025868091301">"全部关闭"</string>
@@ -103,7 +108,7 @@
<string name="every_weekday" msgid="5896574838353902978">"每个工作日(周一至周五)"</string>
<string name="weekly" msgid="2006107373119027114">"每周(每周的<xliff:g id="DAYS_OF_WEEK">%s</xliff:g>)"</string>
<string name="monthly_on_day_count" msgid="6847991854413461795">"每月(每月的<xliff:g id="ORDINAL_NUMBER">%1$s</xliff:g><xliff:g id="DAY_OF_WEEK">%2$s</xliff:g>)"</string>
- <string name="monthly_on_day" msgid="5370155265278821156">"每月(第 <xliff:g id="DAY_OF_MONTH">%s</xliff:g> 日)"</string>
+ <string name="monthly_on_day" msgid="5370155265278821156">"每月(<xliff:g id="DAY_OF_MONTH">%s</xliff:g> 日)"</string>
<string name="yearly" msgid="8576208380302949002">"每年(<xliff:g id="DATES">%s</xliff:g>)"</string>
<string name="custom" msgid="3015205670911301856">"自定义...(无法在手机上自定义)"</string>
<string name="modify_event" msgid="4501745557655992422">"仅更改此活动。"</string>
@@ -120,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"警报和通知"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"振动"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"选择铃声"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"设置默认提醒"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"默认提醒时间"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"默认提醒时间"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 69bd446..5f076e8 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -16,10 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5452708708398908131">"Google 日曆"</string>
- <string name="calendar_plug" msgid="8148614847911924208"><font fgcolor="#ffffffff">"歡迎使用 Google 日曆!"</font>\n"Google 提供您管理每日行程的方法,讓您憑直覺快速安排活動,輕鬆存取所有行程。"</string>
+ <string name="calendar_plug" msgid="4861031284038774007"><font fgcolor="#ffffffff">"歡迎使用「Google 日曆」!"</font>\n"Google 為提供了管理每日行程的好幫手,讓您憑直覺快速安排活動,輕鬆存取所有行程。"</string>
<string name="what_label" msgid="1933578391985810049">"主題"</string>
<string name="when_label" msgid="894636567327261599">"時間"</string>
<string name="where_label" msgid="3472482206065595118">"地點"</string>
+ <string name="attendees_label" msgid="2971281641565729725">"邀請對象"</string>
+ <string name="response_label" msgid="5732943469653254290">"<xliff:g id="RESPONSE_TYPE">%s</xliff:g> (<xliff:g id="GUEST_COUNT">%d</xliff:g>)"</string>
<string name="repeats_label" msgid="7414023871434593196">"重複頻率"</string>
<string name="no_title_label" msgid="7352118984125107858">"(無主題)"</string>
<plurals name="Nminutes">
@@ -59,6 +61,7 @@
<string name="hint_what" msgid="709155115005044531">"活動名稱"</string>
<string name="hint_where" msgid="3116239630502213241">"活動地點"</string>
<string name="hint_description" msgid="4198202812240544553">"活動說明"</string>
+ <string name="hint_attendees" msgid="6036653711383143333">"電子郵件地址"</string>
<string name="creating_event" msgid="8237877638457604455">"正在建立活動..."</string>
<string name="saving_event" msgid="8853425146353785688">"正在儲存活動..."</string>
<string name="loading_calendars_title" msgid="3819254313413902272">"正在載入日曆"</string>
@@ -78,15 +81,19 @@
<string name="edit_event_show_extra_options" msgid="2325854287823989650">"顯示少用選項"</string>
<string name="edit_event_hide_extra_options" msgid="4019082307326855513">"隱藏少用選項"</string>
<string name="description_label" msgid="7193475046717505855">"說明"</string>
- <string name="presence_label" msgid="2155381988163502023">"狀態"</string>
+ <string name="presence_label" msgid="6169724148441175862">"將我的狀態顯示為"</string>
<string name="privacy_label" msgid="9215031965259597335">"隱私權"</string>
<string name="reminders_label" msgid="8345054160145333166">"提醒"</string>
<string name="no_syncable_calendars" msgid="7019488867045436129">"無日曆"</string>
<string name="no_calendars_found" msgid="755379468136462058">"您沒有日曆。"</string>
<string name="view_event_calendar_label" msgid="8503354404461986250">"日曆"</string>
+ <string name="view_event_organizer_label" msgid="2168441329713325834">"主辦人"</string>
<string name="view_event_timezone_label" msgid="6461351857281070935">"當地時區"</string>
<string name="view_event_response_label" msgid="8382281658458798329">"是否參加?"</string>
- <string name="agenda_today" msgid="2405333795044229594">"今日"</string>
+ <string name="agenda_today" msgid="6489447319363439068">"今天 (<xliff:g id="DATE">%1$s</xliff:g>)"</string>
+ <string name="loading" msgid="3772533493214331230">"載入中..."</string>
+ <string name="show_older_events" msgid="4031960000704544741">"顯示「<xliff:g id="OLDEST_SEARCH_RANGE">%1$s</xliff:g>」之後的活動,輕按以查看更多活動。"</string>
+ <string name="show_newer_events" msgid="2647889653936094369">"顯示「<xliff:g id="NEWEST_SEARCH_RANGE">%1$s</xliff:g>」之前的活動,輕按以查看更多活動。"</string>
<string name="num_events" msgid="3351672964607162257">"活動數"</string>
<string name="edit_event_label" msgid="2900418236819088363">"編輯活動"</string>
<string name="delete_label" msgid="874742811918030739">"刪除"</string>
@@ -118,7 +125,7 @@
<string name="preferences_alerts_type_dialog" msgid="2195667763429994271">"警示及通知"</string>
<string name="preferences_alerts_vibrate_title" msgid="7997122314759645350">"震動"</string>
<string name="preferences_alerts_ringtone_title" msgid="7565118268120729644">"選取鈴聲"</string>
- <string name="preferences_default_reminder_title" msgid="103964814265989503">"設定預設提醒時間"</string>
+ <string name="preferences_default_reminder_title" msgid="5188861433342733905">"預設提醒時間"</string>
<string name="preferences_default_reminder_dialog" msgid="2490348857239323412">"預設提醒時間"</string>
<string name="preferences_default_reminder_default" msgid="2903884416749959435">"10"</string>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e49015e..f733096 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -19,7 +19,7 @@
<string name="app_label">Calendar</string>
<!-- This is the welcome message when a user starts Calendar for the first time -->
<string name="calendar_plug"><font fgcolor="#ffffffff">Welcome to Google Calendar!</font>
- \nA Google approach to managing your schedule built on the idea that scheduling events can be more intuitive, efficient and accessible.
+ \nA Google approach to managing your schedule built on the idea that scheduling events can be more intuitive, efficient, and accessible.
</string>
<!-- Shared Labels. These labels are shared among the activities. -->
@@ -30,6 +30,11 @@
<string name="when_label">When</string>
<!-- This is the label for the location of an event -->
<string name="where_label">Where</string>
+ <!-- This is the label for the Guests/Attendees of an event -->
+ <string name="attendees_label">Guests</string>
+ <!-- This is the label for the Guest Responses and count of an event e.g. Yes (3) -->
+ <string name="response_label">"<xliff:g id="response_type">%s</xliff:g> (<xliff:g id="guest_count">%d</xliff:g>)"</string>
+
<!-- Some events repeat daily, weekly, monthly, or yearly. This is the label
for all the choices about how often an event repeats (including the choice
of not repeating). -->
@@ -151,6 +156,8 @@
<string name="hint_where">"Event location"</string>
<!-- Default value of Description field (as a hint to the user) -->
<string name="hint_description">"Event description"</string>
+ <!-- Default value of Attendees/Guests field (as a hint to the user) -->
+ <string name="hint_attendees">"Email addresses"</string>
<string name="creating_event">"Creating event\u2026"</string>
<string name="saving_event">"Saving event\u2026"</string>
<string name="loading_calendars_title">"Loading calendars"</string>
@@ -206,7 +213,7 @@
<string name="description_label">Description</string>
<!-- Label for the 'Presence' of an event, which can be either 'busy' (the default)
or 'available' -->
- <string name="presence_label">Presence</string>
+ <string name="presence_label">Show me as</string>
<!-- Label for the 'Privacy' of an event, which can be either 'private'
or 'public' -->
<string name="privacy_label">Privacy</string>
@@ -219,6 +226,8 @@
<skip />
<!-- Label for which calendar an event is part of -->
<string name="view_event_calendar_label">Calendar</string>
+ <!-- Label for the event organizer -->
+ <string name="view_event_organizer_label">Organizer</string>
<!-- Label for the local timezone -->
<string name="view_event_timezone_label">Local time zone</string>
<!-- Label for whether the user is attending this event. This is shown when
@@ -229,7 +238,13 @@
<!-- Agenda View strings -->
<skip />
<!-- This is shown as part of the heading at the top of a list of today's events. -->
- <string name="agenda_today">Today</string>
+ <string name="agenda_today">Today, <xliff:g id="date">%1$s</xliff:g></string>
+ <!-- This is shown while the calendar events are being loading to the screen. -->
+ <string name="loading">Loading\u2026</string>
+ <!-- This is shown at the top of the agenda view showing the range of events shown. -->
+ <string name="show_older_events">Showing events since <xliff:g id="oldest_search_range">%1$s</xliff:g>. Tap to look for more.</string>
+ <!-- This is shown at the bottom of the agenda view showing the range of events shown. -->
+ <string name="show_newer_events">Showing events until <xliff:g id="newest_search_range">%1$s</xliff:g>. Tap to look for more.</string>
<!-- ICS Import activity -->
<skip />
@@ -343,7 +358,7 @@
<!-- Title of ringtone selector dialog -->
<string name="preferences_alerts_ringtone_title">Select ringtone</string>
<!-- List item label for setting the default number of reminder minutes -->
- <string name="preferences_default_reminder_title">Set default reminder</string>
+ <string name="preferences_default_reminder_title">Default reminder time</string>
<!-- Title of dialog for setting the default number of reminder minutes -->
<string name="preferences_default_reminder_dialog">Default reminder time</string>
<!-- Default value for the number of reminder minutes -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 4471ad2..0f6e89d 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -59,6 +59,13 @@
<style name="TextAppearance.EditEvent_Label">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+ <item name="android:textColor">?android:attr/textColorSecondaryInverse</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:paddingLeft">2dip</item>
+ </style>
+
+ <style name="TextAppearance.EventInfo_Label">
+ <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
<item name="android:textStyle">bold</item>
<item name="android:paddingLeft">2dip</item>
diff --git a/src/com/android/calendar/AgendaActivity.java b/src/com/android/calendar/AgendaActivity.java
index 00a46af..b414033 100644
--- a/src/com/android/calendar/AgendaActivity.java
+++ b/src/com/android/calendar/AgendaActivity.java
@@ -16,163 +16,44 @@
package com.android.calendar;
+import static android.provider.Calendar.EVENT_BEGIN_TIME;
+
import android.app.Activity;
-import android.content.AsyncQueryHandler;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
-import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
-import android.provider.Calendar;
-import android.provider.Calendar.Attendees;
-import android.provider.Calendar.Calendars;
import android.provider.Calendar.Events;
-import android.provider.Calendar.Instances;
import android.text.format.Time;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
-import android.view.Window;
-import android.widget.AdapterView;
-import android.widget.ListView;
-import android.widget.ViewSwitcher;
+
import dalvik.system.VMRuntime;
-public class AgendaActivity extends Activity implements ViewSwitcher.ViewFactory, Navigator {
+public class AgendaActivity extends Activity implements Navigator {
+
+ private static final String TAG = "AgendaActivity";
+
+ private static boolean DEBUG = false;
protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
- static final String[] PROJECTION = new String[] {
- Instances._ID, // 0
- Instances.TITLE, // 1
- Instances.EVENT_LOCATION, // 2
- Instances.ALL_DAY, // 3
- Instances.HAS_ALARM, // 4
- Instances.COLOR, // 5
- Instances.RRULE, // 6
- Instances.BEGIN, // 7
- Instances.END, // 8
- Instances.EVENT_ID, // 9
- Instances.START_DAY, // 10 Julian start day
- Instances.END_DAY, // 11 Julian end day
- Instances.SELF_ATTENDEE_STATUS, // 12
- };
-
- public static final int INDEX_TITLE = 1;
- public static final int INDEX_EVENT_LOCATION = 2;
- public static final int INDEX_ALL_DAY = 3;
- public static final int INDEX_HAS_ALARM = 4;
- public static final int INDEX_COLOR = 5;
- public static final int INDEX_RRULE = 6;
- public static final int INDEX_BEGIN = 7;
- public static final int INDEX_END = 8;
- public static final int INDEX_EVENT_ID = 9;
- public static final int INDEX_START_DAY = 10;
- public static final int INDEX_END_DAY = 11;
- public static final int INDEX_SELF_ATTENDEE_STATUS = 12;
-
- public static final String AGENDA_SORT_ORDER = "startDay ASC, begin ASC, title ASC";
-
private static final long INITIAL_HEAP_SIZE = 4*1024*1024;
private ContentResolver mContentResolver;
- private ViewSwitcher mViewSwitcher;
+ private AgendaListView mAgendaListView;
- private QueryHandler mQueryHandler;
- private DeleteEventHelper mDeleteEventHelper;
private Time mTime;
- /**
- * This records the start time parameter for the last query sent to the
- * AsyncQueryHandler so that we don't send it duplicate query requests.
- */
- private Time mLastQueryTime = new Time();
-
- private class QueryHandler extends AsyncQueryHandler {
- public QueryHandler(ContentResolver cr) {
- super(cr);
- }
-
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
-
- // Only set mCursor if the Activity is not finishing. Otherwise close the cursor.
- if (!isFinishing()) {
- AgendaListView next = (AgendaListView) mViewSwitcher.getNextView();
- next.setCursor(cursor);
- mViewSwitcher.showNext();
- selectTime();
- } else {
- cursor.close();
- }
- }
- }
-
- private class AgendaListView extends ListView {
- private Cursor mCursor;
- private AgendaByDayAdapter mDayAdapter;
- private AgendaAdapter mAdapter;
-
- public AgendaListView(Context context) {
- super(context, null);
- setOnItemClickListener(mOnItemClickListener);
- setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- mAdapter = new AgendaAdapter(AgendaActivity.this, R.layout.agenda_item);
- mDayAdapter = new AgendaByDayAdapter(AgendaActivity.this, mAdapter);
- }
-
- public void setCursor(Cursor cursor) {
- if (mCursor != null) {
- mCursor.close();
- }
- mCursor = cursor;
- mDayAdapter.calculateDays(cursor);
- mAdapter.changeCursor(cursor);
- setAdapter(mDayAdapter);
- }
-
- public Cursor getCursor() {
- return mCursor;
- }
-
- public AgendaByDayAdapter getDayAdapter() {
- return mDayAdapter;
- }
-
- @Override protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mCursor != null) {
- mCursor.close();
- }
- }
-
- private OnItemClickListener mOnItemClickListener = new OnItemClickListener() {
- public void onItemClick(AdapterView a, View v, int position, long id) {
- if (id != -1) {
- // Switch to the EventInfo view
- mCursor.moveToPosition(mDayAdapter.getCursorPosition(position));
- long eventId = mCursor.getLong(INDEX_EVENT_ID);
- Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.putExtra(Calendar.EVENT_BEGIN_TIME, mCursor.getLong(INDEX_BEGIN));
- intent.putExtra(Calendar.EVENT_END_TIME, mCursor.getLong(INDEX_END));
- startActivity(intent);
- }
- }
- };
- }
-
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -180,8 +61,7 @@
if (action.equals(Intent.ACTION_TIME_CHANGED)
|| action.equals(Intent.ACTION_DATE_CHANGED)
|| action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
- clearLastQueryTime();
- renewCursor();
+ mAgendaListView.refresh(true);
}
}
};
@@ -194,56 +74,78 @@
@Override
public void onChange(boolean selfChange) {
- clearLastQueryTime();
- renewCursor();
+ mAgendaListView.refresh(true);
}
};
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
-
+
// Eliminate extra GCs during startup by setting the initial heap size to 4MB.
// TODO: We should restore the old heap size once the activity reaches the idle state
- long oldHeapSize = VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);
+ VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);
- setContentView(R.layout.agenda_activity);
+ mAgendaListView = new AgendaListView(this);
+ setContentView(mAgendaListView);
mContentResolver = getContentResolver();
- mQueryHandler = new QueryHandler(mContentResolver);
- // Preserve the same month and event selection if this activity is
- // being restored due to an orientation change
- mTime = new Time();
- if (icicle != null) {
- mTime.set(icicle.getLong(BUNDLE_KEY_RESTORE_TIME));
- } else {
- mTime.set(Utils.timeFromIntent(getIntent()));
- }
setTitle(R.string.agenda_view);
- mViewSwitcher = (ViewSwitcher) findViewById(R.id.switcher);
- mViewSwitcher.setFactory(this);
+ long millis = 0;
+ mTime = new Time();
+ if (icicle != null) {
+ // Returns 0 if key not found
+ millis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME);
+ if (DEBUG) {
+ Log.v(TAG, "Restore value from icicle: " + millis);
+ }
+ }
- // Record Agenda View as the (new) default detailed view.
- String activityString = CalendarApplication.ACTIVITY_NAMES[CalendarApplication.AGENDA_VIEW_ID];
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(CalendarPreferenceActivity.KEY_DETAILED_VIEW, activityString);
+ if (millis == 0) {
+ // Returns 0 if key not found
+ millis = getIntent().getLongExtra(EVENT_BEGIN_TIME, 0);
+ if (DEBUG) {
+ Time time = new Time();
+ time.set(millis);
+ Log.v(TAG, "Restore value from intent: " + time.toString());
+ }
+ }
- // Record Agenda View as the (new) start view
- editor.putString(CalendarPreferenceActivity.KEY_START_VIEW, activityString);
- editor.commit();
+ if (millis == 0) {
+ if (DEBUG) {
+ Log.v(TAG, "Restored from current time");
+ }
+ millis = System.currentTimeMillis();
+ }
+ mTime.set(millis);
+ }
- mDeleteEventHelper = new DeleteEventHelper(this, false /* don't exit when done */);
+ @Override
+ protected void onNewIntent(Intent intent) {
+ long time = Utils.timeFromIntentInMillis(intent);
+ if (time > 0) {
+ mTime.set(time);
+ goTo(mTime, false);
+ }
}
@Override
protected void onResume() {
super.onResume();
+ if (DEBUG) {
+ Log.v(TAG, "OnResume to " + mTime.toString());
+ }
- clearLastQueryTime();
- renewCursor();
+ SharedPreferences prefs =
+ PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ boolean hideDeclined = prefs
+ .getBoolean(CalendarPreferenceActivity.KEY_HIDE_DECLINED, false);
+
+ mAgendaListView.setHideDeclinedEvents(hideDeclined);
+ mAgendaListView.goTo(mTime, true);
+ mAgendaListView.onResume();
// Register for Intent broadcasts
IntentFilter filter = new IntentFilter();
@@ -259,21 +161,26 @@
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putLong(BUNDLE_KEY_RESTORE_TIME, getSelectedTime());
+ long firstVisibleTime = mAgendaListView.getFirstVisibleTime();
+ if (firstVisibleTime > 0) {
+ mTime.set(firstVisibleTime);
+ outState.putLong(BUNDLE_KEY_RESTORE_TIME, firstVisibleTime);
+ if (DEBUG) {
+ Log.v(TAG, "onSaveInstanceState " + mTime.toString());
+ }
+ }
}
@Override
protected void onPause() {
super.onPause();
+ mAgendaListView.onPause();
mContentResolver.unregisterContentObserver(mObserver);
unregisterReceiver(mIntentReceiver);
- // Clear the cursor so it won't crash when switching orientation while scrolling b/2022729
- String[] columns = new String[1];
- columns[0] = "_id";
- AgendaListView current = (AgendaListView) mViewSwitcher.getCurrentView();
- current.setCursor(new MatrixCursor(columns));
+ // Record Agenda View as the (new) default detailed view.
+ Utils.setDefaultView(this, CalendarApplication.AGENDA_VIEW_ID);
}
@Override
@@ -297,135 +204,27 @@
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
- case KeyEvent.KEYCODE_DEL: {
+ case KeyEvent.KEYCODE_DEL:
// Delete the currently selected event (if any)
- AgendaListView current = (AgendaListView) mViewSwitcher.getCurrentView();
- Cursor cursor = current.getCursor();
- if (cursor != null) {
- int position = current.getSelectedItemPosition();
- position = current.getDayAdapter().getCursorPosition(position);
- if (position >= 0) {
- cursor.moveToPosition(position);
- long begin = cursor.getLong(INDEX_BEGIN);
- long end = cursor.getLong(INDEX_END);
- long eventId = cursor.getLong(INDEX_EVENT_ID);
- mDeleteEventHelper.delete(begin, end, eventId, -1);
- }
- }
- }
+ mAgendaListView.deleteSelectedEvent();
break;
-
- case KeyEvent.KEYCODE_BACK:
- finish();
- return true;
}
return super.onKeyDown(keyCode, event);
}
- /**
- * Clears the cached value for the last query time so that renewCursor()
- * will force a requery of the Calendar events.
- */
- private void clearLastQueryTime() {
- mLastQueryTime.year = 0;
- mLastQueryTime.month = 0;
- }
-
- private void renewCursor() {
- // Avoid batching up repeated queries for the same month. This can
- // happen if the user scrolls with the trackball too fast.
- if (mLastQueryTime.month == mTime.month && mLastQueryTime.year == mTime.year) {
- return;
- }
-
- // Query all instances for the current month
- Time time = new Time();
- time.year = mTime.year;
- time.month = mTime.month;
- long start = time.normalize(true);
-
- time.month++;
- long end = time.normalize(true);
-
- StringBuilder path = new StringBuilder();
- path.append(start);
- path.append('/');
- path.append(end);
-
- // Respect the preference to show/hide declined events
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
- boolean hideDeclined = prefs.getBoolean(CalendarPreferenceActivity.KEY_HIDE_DECLINED,
- false);
-
- Uri uri = Uri.withAppendedPath(Instances.CONTENT_URI, path.toString());
-
- String selection;
- if (hideDeclined) {
- selection = Calendars.SELECTED + "=1 AND " +
- Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED;
- } else {
- selection = Calendars.SELECTED + "=1";
- }
-
- // Cancel any previous queries that haven't started yet. This
- // isn't likely to happen since we already avoid sending
- // a duplicate query for the same month as the previous query.
- // But if the user quickly wiggles the trackball back and forth,
- // he could generate a stream of queries.
- mQueryHandler.cancelOperation(0);
-
- mLastQueryTime.month = mTime.month;
- mLastQueryTime.year = mTime.year;
- mQueryHandler.startQuery(0, null, uri, PROJECTION, selection, null,
- AGENDA_SORT_ORDER);
- }
-
- private void selectTime() {
- // Selects the first event of the day
- AgendaListView current = (AgendaListView) mViewSwitcher.getCurrentView();
- if (current.getCursor() == null) {
- return;
- }
-
- int position = current.getDayAdapter().findDayPositionNearestTime(mTime);
- current.setSelection(position);
- }
-
- /* ViewSwitcher.ViewFactory interface methods */
- public View makeView() {
- AgendaListView agendaListView = new AgendaListView(this);
- return agendaListView;
- }
-
/* Navigator interface methods */
public void goToToday() {
Time now = new Time();
now.set(System.currentTimeMillis());
- goTo(now);
+ goTo(now, true);
}
- public void goTo(Time time) {
- if (mTime.year == time.year && mTime.month == time.month) {
- mTime = time;
- selectTime();
- } else {
- mTime = time;
- renewCursor();
- }
+ public void goTo(Time time, boolean animate) {
+ mAgendaListView.goTo(time, false);
}
public long getSelectedTime() {
- // Update the current time based on the selected event
- AgendaListView current = (AgendaListView) mViewSwitcher.getCurrentView();
- int position = current.getSelectedItemPosition();
- position = current.getDayAdapter().getCursorPosition(position);
- Cursor cursor = current.getCursor();
- if (position >= 0 && position < cursor.getCount()) {
- cursor.moveToPosition(position);
- mTime.set(cursor.getLong(INDEX_BEGIN));
- }
-
- return mTime.toMillis(true);
+ return mAgendaListView.getSelectedTime();
}
public boolean getAllDay() {
diff --git a/src/com/android/calendar/AgendaAdapter.java b/src/com/android/calendar/AgendaAdapter.java
index 1b93b7e..e54e345 100644
--- a/src/com/android/calendar/AgendaAdapter.java
+++ b/src/com/android/calendar/AgendaAdapter.java
@@ -26,10 +26,16 @@
import android.widget.ResourceCursorAdapter;
import android.widget.TextView;
+import java.util.Formatter;
+import java.util.Locale;
+
public class AgendaAdapter extends ResourceCursorAdapter {
- static private String mNoTitleLabel; // todo update on locale change.
+ private String mNoTitleLabel;
private Resources mResources;
private int mDeclinedColor;
+ // Note: Formatter is not thread safe. Fine for now as it is only used by the main thread.
+ private Formatter mFormatter;
+ private StringBuilder mStringBuilder;
static class ViewHolder {
int overLayColor; // Used by AgendaItemView to gray out the entire item if so desired
@@ -46,11 +52,20 @@
mResources = context.getResources();
mNoTitleLabel = mResources.getString(R.string.no_title_label);
mDeclinedColor = mResources.getColor(R.drawable.agenda_item_declined);
+ mStringBuilder = new StringBuilder(50);
+ mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
- ViewHolder holder = (ViewHolder) view.getTag();
+ ViewHolder holder = null;
+
+ // Listview may get confused and pass in a different type of view since
+ // we keep shifting data around. Not a big problem.
+ Object tag = view.getTag();
+ if (tag instanceof ViewHolder) {
+ holder = (ViewHolder) view.getTag();
+ }
if (holder == null) {
holder = new ViewHolder();
@@ -61,7 +76,7 @@
}
// Fade text if event was declined.
- int selfAttendeeStatus = cursor.getInt(AgendaActivity.INDEX_SELF_ATTENDEE_STATUS);
+ int selfAttendeeStatus = cursor.getInt(AgendaWindowAdapter.INDEX_SELF_ATTENDEE_STATUS);
if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) {
holder.overLayColor = mDeclinedColor;
} else {
@@ -73,11 +88,11 @@
TextView where = holder.where;
/* Calendar Color */
- int color = cursor.getInt(AgendaActivity.INDEX_COLOR);
+ int color = cursor.getInt(AgendaWindowAdapter.INDEX_COLOR);
holder.calendarColor = color;
// What
- String titleString = cursor.getString(AgendaActivity.INDEX_TITLE);
+ String titleString = cursor.getString(AgendaWindowAdapter.INDEX_TITLE);
if (titleString == null || titleString.length() == 0) {
titleString = mNoTitleLabel;
}
@@ -85,9 +100,9 @@
title.setTextColor(color);
// When
- long begin = cursor.getLong(AgendaActivity.INDEX_BEGIN);
- long end = cursor.getLong(AgendaActivity.INDEX_END);
- boolean allDay = cursor.getInt(AgendaActivity.INDEX_ALL_DAY) != 0;
+ long begin = cursor.getLong(AgendaWindowAdapter.INDEX_BEGIN);
+ long end = cursor.getLong(AgendaWindowAdapter.INDEX_END);
+ boolean allDay = cursor.getInt(AgendaWindowAdapter.INDEX_ALL_DAY) != 0;
int flags;
String whenString;
if (allDay) {
@@ -98,10 +113,11 @@
if (DateFormat.is24HourFormat(context)) {
flags |= DateUtils.FORMAT_24HOUR;
}
- whenString = DateUtils.formatDateRange(context, begin, end, flags);
+ mStringBuilder.setLength(0);
+ whenString = DateUtils.formatDateRange(context, mFormatter, begin, end, flags).toString();
when.setText(whenString);
- String rrule = cursor.getString(AgendaActivity.INDEX_RRULE);
+ String rrule = cursor.getString(AgendaWindowAdapter.INDEX_RRULE);
if (rrule != null) {
when.setCompoundDrawablesWithIntrinsicBounds(null, null,
context.getResources().getDrawable(R.drawable.ic_repeat_dark), null);
@@ -130,7 +146,7 @@
*/
// Where
- String whereString = cursor.getString(AgendaActivity.INDEX_EVENT_LOCATION);
+ String whereString = cursor.getString(AgendaWindowAdapter.INDEX_EVENT_LOCATION);
if (whereString != null && whereString.length() > 0) {
where.setVisibility(View.VISIBLE);
where.setText(whereString);
diff --git a/src/com/android/calendar/AgendaByDayAdapter.java b/src/com/android/calendar/AgendaByDayAdapter.java
index 140eb72..881c1a2 100644
--- a/src/com/android/calendar/AgendaByDayAdapter.java
+++ b/src/com/android/calendar/AgendaByDayAdapter.java
@@ -26,27 +26,39 @@
import android.widget.BaseAdapter;
import android.widget.TextView;
+import com.android.calendar.AgendaWindowAdapter.DayAdapterInfo;
+
import java.util.ArrayList;
-import java.util.Calendar;
+import java.util.Formatter;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.Locale;
public class AgendaByDayAdapter extends BaseAdapter {
private static final int TYPE_DAY = 0;
private static final int TYPE_MEETING = 1;
- private static final int TYPE_LAST = 2;
+ static final int TYPE_LAST = 2;
private final Context mContext;
private final AgendaAdapter mAgendaAdapter;
private final LayoutInflater mInflater;
private ArrayList<RowInfo> mRowInfo;
private int mTodayJulianDay;
- private Time mTime = new Time();
+ private Time mTmpTime = new Time();
+ // Note: Formatter is not thread safe. Fine for now as it is only used by the main thread.
+ private Formatter mFormatter;
+ private StringBuilder mStringBuilder;
- public AgendaByDayAdapter(Context context, AgendaAdapter agendaAdapter) {
+ static class ViewHolder {
+ TextView dateView;
+ }
+
+ public AgendaByDayAdapter(Context context) {
mContext = context;
- mAgendaAdapter = agendaAdapter;
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mAgendaAdapter = new AgendaAdapter(context, R.layout.agenda_item);
+ mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mStringBuilder = new StringBuilder(50);
+ mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
}
public int getCount() {
@@ -72,7 +84,7 @@
if (mRowInfo != null) {
RowInfo row = mRowInfo.get(position);
if (row.mType == TYPE_DAY) {
- return position;
+ return -position;
} else {
return mAgendaAdapter.getItemId(row.mData);
}
@@ -91,10 +103,6 @@
mRowInfo.get(position).mType : TYPE_DAY;
}
- private static class ViewHolder {
- TextView dateView;
- }
-
public View getView(int position, View convertView, ViewGroup parent) {
if ((mRowInfo == null) || (position > mRowInfo.size())) {
// If we have no row info, mAgendaAdapter returns the view.
@@ -103,38 +111,59 @@
RowInfo row = mRowInfo.get(position);
if (row.mType == TYPE_DAY) {
- ViewHolder holder;
- View agendaDayView;
- if ((convertView == null) || (convertView.getTag() == null)) {
+ ViewHolder holder = null;
+ View agendaDayView = null;
+ if ((convertView != null) && (convertView.getTag() != null)) {
+ // Listview may get confused and pass in a different type of
+ // view since we keep shifting data around. Not a big problem.
+ Object tag = convertView.getTag();
+ if (tag instanceof ViewHolder) {
+ agendaDayView = convertView;
+ holder = (ViewHolder) tag;
+ }
+ }
+
+ if (holder == null) {
// Create a new AgendaView with a ViewHolder for fast access to
// views w/o calling findViewById()
holder = new ViewHolder();
agendaDayView = mInflater.inflate(R.layout.agenda_day, parent, false);
holder.dateView = (TextView) agendaDayView.findViewById(R.id.date);
agendaDayView.setTag(holder);
- } else {
- agendaDayView = convertView;
- holder = (ViewHolder) convertView.getTag();
}
// Re-use the member variable "mTime" which is set to the local timezone.
- Time date = mTime;
+ Time date = mTmpTime;
long millis = date.setJulianDay(row.mData);
int flags = DateUtils.FORMAT_SHOW_YEAR
| DateUtils.FORMAT_SHOW_DATE;
-
+
+ mStringBuilder.setLength(0);
+ String dateViewText;
if (row.mData == mTodayJulianDay) {
- String dayText = mContext.getResources().getText(R.string.agenda_today) + ", ";
- holder.dateView.setText(dayText + DateUtils.formatDateTime(mContext, millis, flags));
+ dateViewText = mContext.getString(R.string.agenda_today, DateUtils.formatDateRange(
+ mContext, mFormatter, millis, millis, flags).toString());
} else {
flags |= DateUtils.FORMAT_SHOW_WEEKDAY;
- holder.dateView.setText(DateUtils.formatDateTime(mContext, millis, flags));
+ dateViewText = DateUtils.formatDateRange(mContext, mFormatter, millis, millis,
+ flags).toString();
}
+ if (AgendaWindowAdapter.BASICLOG) {
+ dateViewText += " P:" + position;
+ }
+ holder.dateView.setText(dateViewText);
return agendaDayView;
} else if (row.mType == TYPE_MEETING) {
- return mAgendaAdapter.getView(row.mData, convertView, parent);
+ View x = mAgendaAdapter.getView(row.mData, convertView, parent);
+ TextView y = ((AgendaAdapter.ViewHolder) x.getTag()).title;
+ if (AgendaWindowAdapter.BASICLOG) {
+ y.setText(y.getText() + " P:" + position);
+ } else {
+ y.setText(y.getText());
+ }
+ return x;
} else {
// Error
throw new IllegalStateException("Unknown event type:" + row.mType);
@@ -145,7 +174,13 @@
mRowInfo = null;
}
- public void calculateDays(Cursor cursor) {
+ public void changeCursor(DayAdapterInfo info) {
+ calculateDays(info);
+ mAgendaAdapter.changeCursor(info.cursor);
+ }
+
+ public void calculateDays(DayAdapterInfo dayAdapterInfo) {
+ Cursor cursor = dayAdapterInfo.cursor;
ArrayList<RowInfo> rowInfo = new ArrayList<RowInfo>();
int prevStartDay = -1;
Time time = new Time();
@@ -154,8 +189,11 @@
mTodayJulianDay = Time.getJulianDay(now, time.gmtoff);
LinkedList<MultipleDayInfo> multipleDayList = new LinkedList<MultipleDayInfo>();
for (int position = 0; cursor.moveToNext(); position++) {
- boolean allDay = cursor.getInt(AgendaActivity.INDEX_ALL_DAY) != 0;
- int startDay = cursor.getInt(AgendaActivity.INDEX_START_DAY);
+ boolean allDay = cursor.getInt(AgendaWindowAdapter.INDEX_ALL_DAY) != 0;
+ int startDay = cursor.getInt(AgendaWindowAdapter.INDEX_START_DAY);
+
+ // Skip over the days outside of the adapter's range
+ startDay = Math.max(startDay, dayAdapterInfo.start);
if (startDay != prevStartDay) {
// Check if we skipped over any empty days
@@ -202,7 +240,10 @@
// If this event spans multiple days, then add it to the multipleDay
// list.
- int endDay = cursor.getInt(AgendaActivity.INDEX_END_DAY);
+ int endDay = cursor.getInt(AgendaWindowAdapter.INDEX_END_DAY);
+
+ // Skip over the days outside of the adapter's range
+ endDay = Math.min(endDay, dayAdapterInfo.end);
if (endDay > startDay) {
multipleDayList.add(new MultipleDayInfo(position, endDay));
}
@@ -211,16 +252,8 @@
// There are no more cursor events but we might still have multiple-day
// events left. So create day headers and events for those.
if (prevStartDay > 0) {
- // Get the Julian day for the last day of this month. To do that,
- // we set the date to one less than the first day of the next month,
- // and then normalize.
- time.setJulianDay(prevStartDay);
- time.month += 1;
- time.monthDay = 0; // monthDay starts with 1, so this is the previous day
- long millis = time.normalize(true /* ignore isDst */);
- int lastDayOfMonth = Time.getJulianDay(millis, time.gmtoff);
-
- for (int currentDay = prevStartDay + 1; currentDay <= lastDayOfMonth; currentDay++) {
+ for (int currentDay = prevStartDay + 1; currentDay <= dayAdapterInfo.end;
+ currentDay++) {
boolean dayHeaderAdded = false;
Iterator<MultipleDayInfo> iter = multipleDayList.iterator();
while (iter.hasNext()) {
@@ -342,9 +375,17 @@
RowInfo row = mRowInfo.get(listPos);
if (row.mType == TYPE_MEETING) {
return row.mData;
+ } else {
+ int nextPos = listPos + 1;
+ if (nextPos < mRowInfo.size()) {
+ nextPos = getCursorPosition(nextPos);
+ if (nextPos >= 0) {
+ return -nextPos;
+ }
+ }
}
}
- return listPos;
+ return Integer.MIN_VALUE;
}
@Override
@@ -361,4 +402,3 @@
return true;
}
}
-
diff --git a/src/com/android/calendar/AgendaListView.java b/src/com/android/calendar/AgendaListView.java
new file mode 100644
index 0000000..f5e63b4
--- /dev/null
+++ b/src/com/android/calendar/AgendaListView.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * 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 the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.calendar;
+
+import android.content.ContentUris;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.provider.Calendar;
+import android.provider.Calendar.Events;
+import android.text.format.Time;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+
+import com.android.calendar.AgendaAdapter.ViewHolder;
+import com.android.calendar.AgendaWindowAdapter.EventInfo;
+
+public class AgendaListView extends ListView implements OnItemClickListener {
+
+ private static final String TAG = "AgendaListView";
+ private static final boolean DEBUG = false;
+
+ private AgendaWindowAdapter mWindowAdapter;
+
+ private AgendaActivity mAgendaActivity;
+ private DeleteEventHelper mDeleteEventHelper;
+
+ public AgendaListView(AgendaActivity agendaActivity) {
+ super(agendaActivity, null);
+ mAgendaActivity = agendaActivity;
+ mContext = agendaActivity;
+
+ setOnItemClickListener(this);
+ setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ setVerticalScrollBarEnabled(false);
+ mWindowAdapter = new AgendaWindowAdapter(agendaActivity, this);
+ setAdapter(mWindowAdapter);
+ mDeleteEventHelper =
+ new DeleteEventHelper(agendaActivity, false /* don't exit when done */);
+ }
+
+ @Override protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mWindowAdapter.close();
+ }
+
+ // Implementation of the interface OnItemClickListener
+ public void onItemClick(AdapterView<?> a, View v, int position, long id) {
+ if (id != -1) {
+ // Switch to the EventInfo view
+ EventInfo event = mWindowAdapter.getEventByPosition(position);
+ if (event != null) {
+ Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, event.id);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.putExtra(Calendar.EVENT_BEGIN_TIME, event.begin);
+ intent.putExtra(Calendar.EVENT_END_TIME, event.end);
+ mAgendaActivity.startActivity(intent);
+ }
+ }
+ }
+
+ public void goTo(Time time, boolean forced) {
+ mWindowAdapter.refresh(time, forced);
+ }
+
+ public void refresh(boolean forced) {
+ Time time = new Time();
+ long goToTime = getFirstVisibleTime();
+ if (goToTime <= 0) {
+ goToTime = System.currentTimeMillis();
+ }
+ time.set(goToTime);
+ mWindowAdapter.refresh(time, forced);
+ }
+
+ public void deleteSelectedEvent() {
+ int position = getSelectedItemPosition();
+ EventInfo event = mWindowAdapter.getEventByPosition(position);
+ if (event != null) {
+ mDeleteEventHelper.delete(event.begin, event.end, event.id, -1);
+ }
+ }
+
+ @Override
+ public int getFirstVisiblePosition() {
+ // TODO File bug!
+ // getFirstVisiblePosition doesn't always return the first visible
+ // item. Sometimes, it is above the visible one.
+ // instead. I loop through the viewgroup children and find the first
+ // visible one. BTW, getFirstVisiblePosition() == getChildAt(0). I
+ // am not looping through the entire list.
+ View v = getFirstVisibleView();
+ if (v != null) {
+ if (DEBUG) {
+ Log.v(TAG, "getFirstVisiblePosition: " + AgendaWindowAdapter.getViewTitle(v));
+ }
+ return getPositionForView(v);
+ }
+ return -1;
+ }
+
+ public View getFirstVisibleView() {
+ Rect r = new Rect();
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ View listItem = getChildAt(i);
+ listItem.getLocalVisibleRect(r);
+ if (r.top >= 0) { // if visible
+ return listItem;
+ }
+ }
+ return null;
+ }
+
+ public long getSelectedTime() {
+ int position = getSelectedItemPosition();
+ if (position >= 0) {
+ EventInfo event = mWindowAdapter.getEventByPosition(position);
+ if (event != null) {
+ return event.begin;
+ }
+ }
+ return getFirstVisibleTime();
+ }
+
+ public long getFirstVisibleTime() {
+ int position = getFirstVisiblePosition();
+ if (DEBUG) {
+ Log.v(TAG, "getFirstVisiblePosition = " + position);
+ }
+
+ EventInfo event = mWindowAdapter.getEventByPosition(position);
+ if (event != null) {
+ return event.begin;
+ }
+ return 0;
+ }
+
+ // Move the currently selected or visible focus down by offset amount.
+ // offset could be negative.
+ public void shiftSelection(int offset) {
+ shiftPosition(offset);
+ int position = getSelectedItemPosition();
+ if (position != INVALID_POSITION) {
+ setSelectionFromTop(position + offset, 0);
+ }
+ }
+
+ private void shiftPosition(int offset) {
+ if (DEBUG) {
+ Log.v(TAG, "Shifting position "+ offset);
+ }
+
+ View firstVisibleItem = getFirstVisibleView();
+
+ if (firstVisibleItem != null) {
+ Rect r = new Rect();
+ firstVisibleItem.getLocalVisibleRect(r);
+ // if r.top is < 0, getChildAt(0) and getFirstVisiblePosition() is
+ // returning an item above the first visible item.
+ int position = getPositionForView(firstVisibleItem);
+ setSelectionFromTop(position + offset, r.top > 0 ? -r.top : r.top);
+ if (DEBUG) {
+ if (firstVisibleItem.getTag() instanceof AgendaAdapter.ViewHolder) {
+ ViewHolder viewHolder = (AgendaAdapter.ViewHolder)firstVisibleItem.getTag();
+ Log.v(TAG, "Shifting from " + position + " by " + offset + ". Title "
+ + viewHolder.title.getText());
+ } else if (firstVisibleItem.getTag() instanceof AgendaByDayAdapter.ViewHolder) {
+ AgendaByDayAdapter.ViewHolder viewHolder =
+ (AgendaByDayAdapter.ViewHolder)firstVisibleItem.getTag();
+ Log.v(TAG, "Shifting from " + position + " by " + offset + ". Date "
+ + viewHolder.dateView.getText());
+ } else if (firstVisibleItem instanceof TextView) {
+ Log.v(TAG, "Shifting: Looking at header here. " + getSelectedItemPosition());
+ }
+ }
+ } else if (getSelectedItemPosition() >= 0) {
+ if (DEBUG) {
+ Log.v(TAG, "Shifting selection from " + getSelectedItemPosition() + " by " + offset);
+ }
+ setSelection(getSelectedItemPosition() + offset);
+ }
+ }
+
+ public void setHideDeclinedEvents(boolean hideDeclined) {
+ mWindowAdapter.setHideDeclinedEvents(hideDeclined);
+ }
+
+ public void onResume() {
+ mWindowAdapter.notifyDataSetChanged();
+ }
+ public void onPause() {
+ mWindowAdapter.notifyDataSetInvalidated();
+ }
+}
diff --git a/src/com/android/calendar/AgendaWindowAdapter.java b/src/com/android/calendar/AgendaWindowAdapter.java
new file mode 100644
index 0000000..9d4fb46
--- /dev/null
+++ b/src/com/android/calendar/AgendaWindowAdapter.java
@@ -0,0 +1,886 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * 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 the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.calendar;
+
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Calendar.Attendees;
+import android.provider.Calendar.Calendars;
+import android.provider.Calendar.Instances;
+import android.text.format.DateUtils;
+import android.text.format.Time;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.Formatter;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Locale;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/*
+Bugs Bugs Bugs:
+- At rotation and launch time, the initial position is not set properly. This code is calling
+ listview.setSelection() in 2 rapid secessions but it dropped or didn't process the first one.
+- Scroll using trackball isn't repositioning properly after a new adapter is added.
+- Track ball clicks at the header/footer doesn't work.
+- Potential ping pong effect if the prefetch window is big and data is limited
+- Add index in calendar provider
+
+ToDo ToDo ToDo:
+Get design of header and footer from designer
+
+Make scrolling smoother.
+Test for correctness
+Loading speed
+Check for leaks and excessive allocations
+ */
+
+public class AgendaWindowAdapter extends BaseAdapter {
+
+ static final boolean BASICLOG = false;
+ static final boolean DEBUGLOG = false;
+ private static String TAG = "AgendaWindowAdapter";
+
+ private static final String AGENDA_SORT_ORDER = "startDay ASC, begin ASC, title ASC";
+ public static final int INDEX_TITLE = 1;
+ public static final int INDEX_EVENT_LOCATION = 2;
+ public static final int INDEX_ALL_DAY = 3;
+ public static final int INDEX_HAS_ALARM = 4;
+ public static final int INDEX_COLOR = 5;
+ public static final int INDEX_RRULE = 6;
+ public static final int INDEX_BEGIN = 7;
+ public static final int INDEX_END = 8;
+ public static final int INDEX_EVENT_ID = 9;
+ public static final int INDEX_START_DAY = 10;
+ public static final int INDEX_END_DAY = 11;
+ public static final int INDEX_SELF_ATTENDEE_STATUS = 12;
+
+ private static final String[] PROJECTION = new String[] {
+ Instances._ID, // 0
+ Instances.TITLE, // 1
+ Instances.EVENT_LOCATION, // 2
+ Instances.ALL_DAY, // 3
+ Instances.HAS_ALARM, // 4
+ Instances.COLOR, // 5
+ Instances.RRULE, // 6
+ Instances.BEGIN, // 7
+ Instances.END, // 8
+ Instances.EVENT_ID, // 9
+ Instances.START_DAY, // 10 Julian start day
+ Instances.END_DAY, // 11 Julian end day
+ Instances.SELF_ATTENDEE_STATUS, // 12
+ };
+
+ // Listview may have a bug where the index/position is not consistent when there's a header.
+ // TODO Need to look into this.
+ private static final int OFF_BY_ONE_BUG = 1;
+
+ private static final int MAX_NUM_OF_ADAPTERS = 5;
+
+ private static final int IDEAL_NUM_OF_EVENTS = 50;
+
+ private static final int MIN_QUERY_DURATION = 7; // days
+
+ private static final int MAX_QUERY_DURATION = 60; // days
+
+ private static final int PREFETCH_BOUNDARY = 1;
+
+ // Times to auto-expand/retry query after getting no data
+ private static final int RETRIES_ON_NO_DATA = 0;
+
+ private Context mContext;
+
+ private QueryHandler mQueryHandler;
+
+ private AgendaListView mAgendaListView;
+
+ private int mRowCount; // The sum of the rows in all the adapters
+
+ private int mEmptyCursorCount;
+
+ private DayAdapterInfo mLastUsedInfo; // Cached value of the last used adapter.
+
+ private LinkedList<DayAdapterInfo> mAdapterInfos = new LinkedList<DayAdapterInfo>();
+
+ private ConcurrentLinkedQueue<QuerySpec> mQueryQueue = new ConcurrentLinkedQueue<QuerySpec>();
+
+ private TextView mHeaderView;
+
+ private TextView mFooterView;
+
+ private boolean mDoneSettingUpHeaderFooter = false;
+
+ /*
+ * When the user scrolled to the top, a query will be made for older events
+ * and this will be incremented. Don't make more requests if
+ * mOlderRequests > mOlderRequestsProcessed.
+ */
+ private int mOlderRequests;
+
+ // Number of "older" query that has been processed.
+ private int mOlderRequestsProcessed;
+
+ /*
+ * When the user scrolled to the bottom, a query will be made for newer
+ * events and this will be incremented. Don't make more requests if
+ * mNewerRequests > mNewerRequestsProcessed.
+ */
+ private int mNewerRequests;
+
+ // Number of "newer" query that has been processed.
+ private int mNewerRequestsProcessed;
+
+ // Note: Formatter is not thread safe. Fine for now as it is only used by the main thread.
+ private Formatter mFormatter;
+ private StringBuilder mStringBuilder;
+
+ private boolean mShuttingDown;
+ private boolean mHideDeclined;
+
+ // Types of Query
+ private static final int QUERY_TYPE_OLDER = 0; // Query for older events
+ private static final int QUERY_TYPE_NEWER = 1; // Query for newer events
+ private static final int QUERY_TYPE_CLEAN = 2; // Delete everything and query around a date
+
+ private static class QuerySpec {
+ long queryStartMillis;
+
+ Time goToTime;
+
+ int start;
+
+ int end;
+
+ int queryType;
+
+ public QuerySpec(int queryType) {
+ this.queryType = queryType;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + end;
+ result = prime * result + (int) (queryStartMillis ^ (queryStartMillis >>> 32));
+ result = prime * result + queryType;
+ result = prime * result + start;
+ if (goToTime != null) {
+ long goToTimeMillis = goToTime.toMillis(false);
+ result = prime * result + (int) (goToTimeMillis ^ (goToTimeMillis >>> 32));
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ QuerySpec other = (QuerySpec) obj;
+ if (end != other.end || queryStartMillis != other.queryStartMillis
+ || queryType != other.queryType || start != other.start) {
+ return false;
+ }
+ if (goToTime != null) {
+ if (goToTime.toMillis(false) != other.goToTime.toMillis(false)) {
+ return false;
+ }
+ } else {
+ if (other.goToTime != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ static class EventInfo {
+ long begin;
+
+ long end;
+
+ long id;
+ }
+
+ static class DayAdapterInfo {
+ Cursor cursor;
+
+ AgendaByDayAdapter dayAdapter;
+
+ int start; // start day of the cursor's coverage
+
+ int end; // end day of the cursor's coverage
+
+ int offset; // offset in position in the list view
+
+ int size; // dayAdapter.getCount()
+
+ public DayAdapterInfo(Context context) {
+ dayAdapter = new AgendaByDayAdapter(context);
+ }
+
+ @Override
+ public String toString() {
+ Time time = new Time();
+ StringBuilder sb = new StringBuilder();
+ time.setJulianDay(start);
+ time.normalize(false);
+ sb.append("Start:").append(time.toString());
+ time.setJulianDay(end);
+ time.normalize(false);
+ sb.append(" End:").append(time.toString());
+ sb.append(" Offset:").append(offset);
+ sb.append(" Size:").append(size);
+ return sb.toString();
+ }
+ }
+
+ public AgendaWindowAdapter(AgendaActivity agendaActivity,
+ AgendaListView agendaListView) {
+ mContext = agendaActivity;
+ mAgendaListView = agendaListView;
+ mQueryHandler = new QueryHandler(agendaActivity.getContentResolver());
+
+ mStringBuilder = new StringBuilder(50);
+ mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
+
+ LayoutInflater inflater = (LayoutInflater) agendaActivity
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mHeaderView = (TextView)inflater.inflate(R.layout.agenda_header_footer, null);
+ mFooterView = (TextView)inflater.inflate(R.layout.agenda_header_footer, null);
+ mHeaderView.setText(R.string.loading);
+ mAgendaListView.addHeaderView(mHeaderView);
+ }
+
+ // Method in Adapter
+ @Override
+ public int getViewTypeCount() {
+ return AgendaByDayAdapter.TYPE_LAST;
+ }
+
+ // Method in BaseAdapter
+ @Override
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ // Method in Adapter
+ @Override
+ public int getItemViewType(int position) {
+ DayAdapterInfo info = getAdapterInfoByPosition(position);
+ if (info != null) {
+ return info.dayAdapter.getItemViewType(position - info.offset);
+ } else {
+ return -1;
+ }
+ }
+
+ // Method in BaseAdapter
+ @Override
+ public boolean isEnabled(int position) {
+ DayAdapterInfo info = getAdapterInfoByPosition(position);
+ if (info != null) {
+ return info.dayAdapter.isEnabled(position - info.offset);
+ } else {
+ return false;
+ }
+ }
+
+ // Abstract Method in BaseAdapter
+ public int getCount() {
+ return mRowCount;
+ }
+
+ // Abstract Method in BaseAdapter
+ public Object getItem(int position) {
+ DayAdapterInfo info = getAdapterInfoByPosition(position);
+ if (info != null) {
+ return info.dayAdapter.getItem(position - info.offset);
+ } else {
+ return null;
+ }
+ }
+
+ // Method in BaseAdapter
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ // Abstract Method in BaseAdapter
+ public long getItemId(int position) {
+ DayAdapterInfo info = getAdapterInfoByPosition(position);
+ if (info != null) {
+ return ((position - info.offset) << 20) + info.start ;
+ } else {
+ return -1;
+ }
+ }
+
+ // Abstract Method in BaseAdapter
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (position >= (mRowCount - PREFETCH_BOUNDARY)
+ && mNewerRequests <= mNewerRequestsProcessed) {
+ if (DEBUGLOG) Log.e(TAG, "queryForNewerEvents: ");
+ mNewerRequests++;
+ queueQuery(new QuerySpec(QUERY_TYPE_NEWER));
+ }
+
+ if (position < PREFETCH_BOUNDARY
+ && mOlderRequests <= mOlderRequestsProcessed) {
+ if (DEBUGLOG) Log.e(TAG, "queryForOlderEvents: ");
+ mOlderRequests++;
+ queueQuery(new QuerySpec(QUERY_TYPE_OLDER));
+ }
+
+ View v;
+ DayAdapterInfo info = getAdapterInfoByPosition(position);
+ if (info != null) {
+ v = info.dayAdapter.getView(position - info.offset, convertView,
+ parent);
+ } else {
+ //TODO
+ Log.e(TAG, "BUG: getAdapterInfoByPosition returned null!!! " + position);
+ TextView tv = new TextView(mContext);
+ tv.setText("Bug! " + position);
+ v = tv;
+ }
+
+ if (DEBUGLOG) {
+ Log.e(TAG, "getView " + position + " = " + getViewTitle(v));
+ }
+ return v;
+ }
+
+ private int findDayPositionNearestTime(Time time) {
+ if (DEBUGLOG) Log.e(TAG, "findDayPositionNearestTime " + time);
+
+ DayAdapterInfo info = getAdapterInfoByTime(time);
+ if (info != null) {
+ return info.offset + info.dayAdapter.findDayPositionNearestTime(time);
+ } else {
+ return -1;
+ }
+ }
+
+ private DayAdapterInfo getAdapterInfoByPosition(int position) {
+ synchronized (mAdapterInfos) {
+ if (mLastUsedInfo != null && mLastUsedInfo.offset <= position
+ && position < (mLastUsedInfo.offset + mLastUsedInfo.size)) {
+ return mLastUsedInfo;
+ }
+ for (DayAdapterInfo info : mAdapterInfos) {
+ if (info.offset <= position
+ && position < (info.offset + info.size)) {
+ mLastUsedInfo = info;
+ return info;
+ }
+ }
+ }
+ return null;
+ }
+
+ private DayAdapterInfo getAdapterInfoByTime(Time time) {
+ if (DEBUGLOG) Log.e(TAG, "getAdapterInfoByTime " + time.toString());
+
+ Time tmpTime = new Time(time);
+ long timeInMillis = tmpTime.normalize(true);
+ int day = Time.getJulianDay(timeInMillis, tmpTime.gmtoff);
+ synchronized (mAdapterInfos) {
+ for (DayAdapterInfo info : mAdapterInfos) {
+ if (info.start <= day && day < info.end) {
+ return info;
+ }
+ }
+ }
+ return null;
+ }
+
+ public EventInfo getEventByPosition(int position) {
+ if (DEBUGLOG) Log.e(TAG, "getEventByPosition " + position);
+
+ EventInfo event = new EventInfo();
+ position -= OFF_BY_ONE_BUG;
+ DayAdapterInfo info = getAdapterInfoByPosition(position);
+ if (info == null) {
+ return null;
+ }
+
+ position = info.dayAdapter.getCursorPosition(position - info.offset);
+ if (position == Integer.MIN_VALUE) {
+ return null;
+ }
+
+ boolean isDayHeader = false;
+ if (position < 0) {
+ position = -position;
+ isDayHeader = true;
+ }
+
+ if (position < info.cursor.getCount()) {
+ info.cursor.moveToPosition(position);
+ event.begin = info.cursor.getLong(AgendaWindowAdapter.INDEX_BEGIN);
+ boolean allDay = info.cursor.getInt(AgendaWindowAdapter.INDEX_ALL_DAY) != 0;
+
+ if (allDay) { // UTC
+ Time time = new Time();
+ time.setJulianDay(Time.getJulianDay(event.begin, 0));
+ event.begin = time.toMillis(false /* use isDst */);
+ } else if (isDayHeader) { // Trim to midnight.
+ Time time = new Time();
+ time.set(event.begin);
+ time.hour = 0;
+ time.minute = 0;
+ time.second = 0;
+ event.begin = time.toMillis(false /* use isDst */);
+ }
+
+ if (!isDayHeader) {
+ event.end = info.cursor.getLong(AgendaWindowAdapter.INDEX_END);
+ event.id = info.cursor.getLong(AgendaWindowAdapter.INDEX_EVENT_ID);
+ }
+ return event;
+ }
+ return null;
+ }
+
+ public void refresh(Time goToTime, boolean forced) {
+ if (DEBUGLOG) {
+ Log.e(TAG, "refresh " + goToTime.toString() + (forced ? " forced" : " not forced"));
+ }
+
+ int startDay = Time.getJulianDay(goToTime.toMillis(false), goToTime.gmtoff);
+
+ if (!forced && isInRange(startDay, startDay)) {
+ // No need to requery
+ mAgendaListView.setSelection(findDayPositionNearestTime(goToTime) + OFF_BY_ONE_BUG);
+ return;
+ }
+
+ // Query for a total of MIN_QUERY_DURATION days
+ int endDay = startDay + MIN_QUERY_DURATION;
+
+ queueQuery(startDay, endDay, goToTime, QUERY_TYPE_CLEAN);
+ }
+
+ public void close() {
+ mShuttingDown = true;
+ pruneAdapterInfo(QUERY_TYPE_CLEAN);
+ if (mQueryHandler != null) {
+ mQueryHandler.cancelOperation(0);
+ }
+ }
+
+ private DayAdapterInfo pruneAdapterInfo(int queryType) {
+ synchronized (mAdapterInfos) {
+ DayAdapterInfo recycleMe = null;
+ if (!mAdapterInfos.isEmpty()) {
+ if (mAdapterInfos.size() >= MAX_NUM_OF_ADAPTERS) {
+ if (queryType == QUERY_TYPE_NEWER) {
+ recycleMe = mAdapterInfos.removeFirst();
+ } else if (queryType == QUERY_TYPE_OLDER) {
+ recycleMe = mAdapterInfos.removeLast();
+ // Keep the size only if the oldest items are removed.
+ recycleMe.size = 0;
+ }
+ if (recycleMe != null) {
+ if (recycleMe.cursor != null) {
+ recycleMe.cursor.close();
+ }
+ return recycleMe;
+ }
+ }
+
+ if (mRowCount == 0 || queryType == QUERY_TYPE_CLEAN) {
+ mRowCount = 0;
+ int deletedRows = 0;
+ DayAdapterInfo info;
+ do {
+ info = mAdapterInfos.poll();
+ if (info != null) {
+ info.cursor.close();
+ deletedRows += info.size;
+ recycleMe = info;
+ }
+ } while (info != null);
+
+ if (recycleMe != null) {
+ recycleMe.cursor = null;
+ recycleMe.size = deletedRows;
+ }
+ }
+ }
+ return recycleMe;
+ }
+ }
+
+ private String buildQuerySelection() {
+ // Respect the preference to show/hide declined events
+
+ if (mHideDeclined) {
+ return Calendars.SELECTED + "=1 AND "
+ + Instances.SELF_ATTENDEE_STATUS + "!="
+ + Attendees.ATTENDEE_STATUS_DECLINED;
+ } else {
+ return Calendars.SELECTED + "=1";
+ }
+ }
+
+ private Uri buildQueryUri(int start, int end) {
+ StringBuilder path = new StringBuilder();
+ path.append(start);
+ path.append('/');
+ path.append(end);
+ Uri uri = Uri.withAppendedPath(Instances.CONTENT_BY_DAY_URI, path.toString());
+ return uri;
+ }
+
+ private boolean isInRange(int start, int end) {
+ synchronized (mAdapterInfos) {
+ if (mAdapterInfos.isEmpty()) {
+ return false;
+ }
+ return mAdapterInfos.getFirst().start <= start && end <= mAdapterInfos.getLast().end;
+ }
+ }
+
+ private int calculateQueryDuration(int start, int end) {
+ int queryDuration = MAX_QUERY_DURATION;
+ if (mRowCount != 0) {
+ queryDuration = IDEAL_NUM_OF_EVENTS * (end - start + 1) / mRowCount;
+ }
+
+ if (queryDuration > MAX_QUERY_DURATION) {
+ queryDuration = MAX_QUERY_DURATION;
+ } else if (queryDuration < MIN_QUERY_DURATION) {
+ queryDuration = MIN_QUERY_DURATION;
+ }
+
+ return queryDuration;
+ }
+
+ private boolean queueQuery(int start, int end, Time goToTime, int queryType) {
+ QuerySpec queryData = new QuerySpec(queryType);
+ queryData.goToTime = goToTime;
+ queryData.start = start;
+ queryData.end = end;
+ return queueQuery(queryData);
+ }
+
+ private boolean queueQuery(QuerySpec queryData) {
+ Boolean queuedQuery;
+ synchronized (mQueryQueue) {
+ queuedQuery = false;
+ Boolean doQueryNow = mQueryQueue.isEmpty();
+ mQueryQueue.add(queryData);
+ queuedQuery = true;
+ if (doQueryNow) {
+ doQuery(queryData);
+ }
+ }
+ return queuedQuery;
+ }
+
+ private void doQuery(QuerySpec queryData) {
+ if (!mAdapterInfos.isEmpty()) {
+ int start = mAdapterInfos.getFirst().start;
+ int end = mAdapterInfos.getLast().end;
+ int queryDuration = calculateQueryDuration(start, end);
+ switch(queryData.queryType) {
+ case QUERY_TYPE_OLDER:
+ queryData.end = start - 1;
+ queryData.start = queryData.end - queryDuration;
+ break;
+ case QUERY_TYPE_NEWER:
+ queryData.start = end + 1;
+ queryData.end = queryData.start + queryDuration;
+ break;
+ }
+ }
+
+ if (BASICLOG) {
+ Time time = new Time();
+ time.setJulianDay(queryData.start);
+ Time time2 = new Time();
+ time2.setJulianDay(queryData.end);
+ Log.v(TAG, "startQuery: " + time.toString() + " to "
+ + time2.toString() + " then go to " + queryData.goToTime);
+ }
+
+ mQueryHandler.cancelOperation(0);
+ if (BASICLOG) queryData.queryStartMillis = System.nanoTime();
+ mQueryHandler.startQuery(0, queryData, buildQueryUri(
+ queryData.start, queryData.end), PROJECTION,
+ buildQuerySelection(), null, AGENDA_SORT_ORDER);
+ }
+
+ private String formatDateString(int julianDay) {
+ Time time = new Time();
+ time.setJulianDay(julianDay);
+ long millis = time.toMillis(false);
+ mStringBuilder.setLength(0);
+ return DateUtils.formatDateRange(mContext, mFormatter, millis, millis,
+ DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE
+ | DateUtils.FORMAT_ABBREV_MONTH).toString();
+ }
+
+ private void updateHeaderFooter(final int start, final int end) {
+ mHeaderView.setText(mContext.getString(R.string.show_older_events,
+ formatDateString(start)));
+ mFooterView.setText(mContext.getString(R.string.show_newer_events,
+ formatDateString(end)));
+ }
+
+ private class QueryHandler extends AsyncQueryHandler {
+
+ public QueryHandler(ContentResolver cr) {
+ super(cr);
+ }
+
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ QuerySpec data = (QuerySpec)cookie;
+ if (BASICLOG) {
+ long queryEndMillis = System.nanoTime();
+ Log.e(TAG, "Query time(ms): "
+ + (queryEndMillis - data.queryStartMillis) / 1000000
+ + " Count: " + cursor.getCount());
+ }
+
+ if (mShuttingDown) {
+ cursor.close();
+ return;
+ }
+
+ // Notify Listview of changes and update position
+ int cursorSize = cursor.getCount();
+ if (cursorSize > 0 || mAdapterInfos.isEmpty()) {
+ final int listPositionOffset = processNewCursor(data, cursor);
+ if (data.goToTime == null) { // Typical Scrolling type query
+ notifyDataSetChanged();
+ if (listPositionOffset != 0) {
+ mAgendaListView.shiftSelection(listPositionOffset);
+ }
+ } else { // refresh() called. Go to the designated position
+ final Time goToTime = data.goToTime;
+ notifyDataSetChanged();
+ int newPosition = findDayPositionNearestTime(goToTime);
+ if (newPosition >= 0) {
+ mAgendaListView.setSelection(newPosition + OFF_BY_ONE_BUG);
+ }
+ if (DEBUGLOG)
+ Log.e(TAG, "Setting listview to " +
+ "findDayPositionNearestTime: " + (newPosition + OFF_BY_ONE_BUG));
+ }
+ } else {
+ cursor.close();
+ }
+
+ // Update header and footer
+ if (!mDoneSettingUpHeaderFooter) {
+ OnClickListener headerFooterOnClickListener = new OnClickListener() {
+ public void onClick(View v) {
+ if (v == mHeaderView) {
+ queueQuery(new QuerySpec(QUERY_TYPE_OLDER));
+ } else {
+ queueQuery(new QuerySpec(QUERY_TYPE_NEWER));
+ }
+ }};
+ mHeaderView.setOnClickListener(headerFooterOnClickListener);
+ mFooterView.setOnClickListener(headerFooterOnClickListener);
+ mAgendaListView.addFooterView(mFooterView);
+ mDoneSettingUpHeaderFooter = true;
+ }
+ synchronized (mQueryQueue) {
+ int totalAgendaRangeStart = -1;
+ int totalAgendaRangeEnd = -1;
+
+ if (cursorSize != 0) {
+ // Remove the query that just completed
+ QuerySpec x = mQueryQueue.poll();
+ if (BASICLOG && !x.equals(data)) {
+ Log.e(TAG, "onQueryComplete - cookie != head of queue");
+ }
+ mEmptyCursorCount = 0;
+ if (data.queryType == QUERY_TYPE_NEWER) {
+ mNewerRequestsProcessed++;
+ } else if (data.queryType == QUERY_TYPE_OLDER) {
+ mOlderRequestsProcessed++;
+ }
+
+ totalAgendaRangeStart = mAdapterInfos.getFirst().start;
+ totalAgendaRangeEnd = mAdapterInfos.getLast().end;
+ } else { // CursorSize == 0
+ QuerySpec querySpec = mQueryQueue.peek();
+
+ // Update Adapter Info with new start and end date range
+ if (!mAdapterInfos.isEmpty()) {
+ DayAdapterInfo first = mAdapterInfos.getFirst();
+ DayAdapterInfo last = mAdapterInfos.getLast();
+
+ if (first.start - 1 <= querySpec.end && querySpec.start < first.start) {
+ first.start = querySpec.start;
+ }
+
+ if (querySpec.start <= last.end + 1 && last.end < querySpec.end) {
+ last.end = querySpec.end;
+ }
+
+ totalAgendaRangeStart = first.start;
+ totalAgendaRangeEnd = last.end;
+ } else {
+ totalAgendaRangeStart = querySpec.start;
+ totalAgendaRangeEnd = querySpec.end;
+ }
+
+ // Update query specification with expanded search range
+ // and maybe rerun query
+ switch (querySpec.queryType) {
+ case QUERY_TYPE_OLDER:
+ totalAgendaRangeStart = querySpec.start;
+ querySpec.start -= MAX_QUERY_DURATION;
+ break;
+ case QUERY_TYPE_NEWER:
+ totalAgendaRangeEnd = querySpec.end;
+ querySpec.end += MAX_QUERY_DURATION;
+ break;
+ case QUERY_TYPE_CLEAN:
+ totalAgendaRangeStart = querySpec.start;
+ totalAgendaRangeEnd = querySpec.end;
+ querySpec.start -= MAX_QUERY_DURATION / 2;
+ querySpec.end += MAX_QUERY_DURATION / 2;
+ break;
+ }
+
+ if (++mEmptyCursorCount > RETRIES_ON_NO_DATA) {
+ // Nothing in the cursor again. Dropping query
+ mQueryQueue.poll();
+ }
+ }
+
+ updateHeaderFooter(totalAgendaRangeStart, totalAgendaRangeEnd);
+
+ // Fire off the next query if any
+ Iterator<QuerySpec> it = mQueryQueue.iterator();
+ while (it.hasNext()) {
+ QuerySpec queryData = it.next();
+ if (!isInRange(queryData.start, queryData.end)) {
+ // Query accepted
+ if (DEBUGLOG) Log.e(TAG, "Query accepted. QueueSize:" + mQueryQueue.size());
+ doQuery(queryData);
+ break;
+ } else {
+ // Query rejected
+ it.remove();
+ if (DEBUGLOG) Log.e(TAG, "Query rejected. QueueSize:" + mQueryQueue.size());
+ }
+ }
+ }
+ if (BASICLOG) {
+ for (DayAdapterInfo info3 : mAdapterInfos) {
+ Log.e(TAG, "> " + info3.toString());
+ }
+ }
+ }
+
+ /*
+ * Update the adapter info array with a the new cursor. Close out old
+ * cursors as needed.
+ *
+ * @return number of rows removed from the beginning
+ */
+ private int processNewCursor(QuerySpec data, Cursor cursor) {
+ synchronized (mAdapterInfos) {
+ // Remove adapter info's from adapterInfos as needed
+ DayAdapterInfo info = pruneAdapterInfo(data.queryType);
+ int listPositionOffset = 0;
+ if (info == null) {
+ info = new DayAdapterInfo(mContext);
+ } else {
+ if (DEBUGLOG)
+ Log.e(TAG, "processNewCursor listPositionOffsetA="
+ + -info.size);
+ listPositionOffset = -info.size;
+ }
+
+ // Setup adapter info
+ info.start = data.start;
+ info.end = data.end;
+ info.cursor = cursor;
+ info.dayAdapter.changeCursor(info);
+ info.size = info.dayAdapter.getCount();
+
+ // Insert into adapterInfos
+ if (mAdapterInfos.isEmpty()
+ || data.end <= mAdapterInfos.getFirst().start) {
+ mAdapterInfos.addFirst(info);
+ listPositionOffset += info.size;
+ } else if (BASICLOG && data.start < mAdapterInfos.getLast().end) {
+ mAdapterInfos.addLast(info);
+ for (DayAdapterInfo info2 : mAdapterInfos) {
+ Log.e("========== BUG ==", info2.toString());
+ }
+ } else {
+ mAdapterInfos.addLast(info);
+ }
+
+ // Update offsets in adapterInfos
+ mRowCount = 0;
+ for (DayAdapterInfo info3 : mAdapterInfos) {
+ info3.offset = mRowCount;
+ mRowCount += info3.size;
+ }
+ mLastUsedInfo = null;
+
+ return listPositionOffset;
+ }
+ }
+ }
+
+ static String getViewTitle(View x) {
+ String title = "";
+ if (x != null) {
+ Object yy = x.getTag();
+ if (yy instanceof AgendaAdapter.ViewHolder) {
+ TextView tv = ((AgendaAdapter.ViewHolder) yy).title;
+ if (tv != null) {
+ title = (String) tv.getText();
+ }
+ } else if (yy != null) {
+ TextView dateView = ((AgendaByDayAdapter.ViewHolder) yy).dateView;
+ if (dateView != null) {
+ title = (String) dateView.getText();
+ }
+ }
+ }
+ return title;
+ }
+
+ public void setHideDeclinedEvents(boolean hideDeclined) {
+ mHideDeclined = hideDeclined;
+ }
+}
diff --git a/src/com/android/calendar/AlertActivity.java b/src/com/android/calendar/AlertActivity.java
index dcf9186..2a041bf 100644
--- a/src/com/android/calendar/AlertActivity.java
+++ b/src/com/android/calendar/AlertActivity.java
@@ -219,7 +219,6 @@
a.recycle();
getWindow().setAttributes(lp);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
mResolver = getContentResolver();
mQueryHandler = new QueryHandler(mResolver);
diff --git a/src/com/android/calendar/AlertService.java b/src/com/android/calendar/AlertService.java
index 558a8f6..29170a9 100644
--- a/src/com/android/calendar/AlertService.java
+++ b/src/com/android/calendar/AlertService.java
@@ -51,11 +51,11 @@
*/
public class AlertService extends Service {
private static final String TAG = "AlertService";
-
+
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
-
- private static final String[] ALERT_PROJECTION = new String[] {
+
+ private static final String[] ALERT_PROJECTION = new String[] {
CalendarAlerts._ID, // 0
CalendarAlerts.EVENT_ID, // 1
CalendarAlerts.STATE, // 2
@@ -69,10 +69,10 @@
};
// We just need a simple projection that returns any column
- private static final String[] ALERT_PROJECTION_SMALL = new String[] {
+ private static final String[] ALERT_PROJECTION_SMALL = new String[] {
CalendarAlerts._ID, // 0
};
-
+
private static final int ALERT_INDEX_ID = 0;
private static final int ALERT_INDEX_EVENT_ID = 1;
private static final int ALERT_INDEX_STATE = 2;
@@ -89,14 +89,30 @@
private static final int INSTANCES_INDEX_END = 1;
// We just need a simple projection that returns any column
- private static final String[] REMINDER_PROJECTION_SMALL = new String[] {
+ private static final String[] REMINDER_PROJECTION_SMALL = new String[] {
Reminders._ID, // 0
};
-
+
+ private final boolean alarmsFiredRecently(ContentResolver cr) {
+ String selection = CalendarAlerts.RECEIVED_TIME + ">="
+ + (System.currentTimeMillis() - 10000);
+ String[] projection = new String[] { CalendarAlerts.ALARM_TIME };
+ Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection, selection, null, null);
+
+ boolean recentAlarms = false;
+ if (cursor != null) {
+ if (cursor.moveToFirst() && cursor.getCount() > 0) {
+ recentAlarms = true;
+ }
+ cursor.close();
+ }
+ return recentAlarms;
+ }
+
@SuppressWarnings("deprecation")
void processMessage(Message msg) {
Bundle bundle = (Bundle) msg.obj;
-
+
// On reboot, update the notification bar with the contents of the
// CalendarAlerts table.
String action = bundle.getString("action");
@@ -112,19 +128,26 @@
Log.d(TAG, "uri: " + alertUri);
}
+ ContentResolver cr = getContentResolver();
+ boolean alarmsFiredRecently = alarmsFiredRecently(cr);
+
if (alertUri != null) {
+ if (!Calendar.AUTHORITY.equals(alertUri.getAuthority())) {
+ Log.w(TAG, "Invalid AUTHORITY uri: " + alertUri);
+ return;
+ }
+
// Record the received time in the CalendarAlerts table.
// This is useful for finding bugs that cause alarms to be
// missed or delayed.
ContentValues values = new ContentValues();
values.put(CalendarAlerts.RECEIVED_TIME, System.currentTimeMillis());
- getContentResolver().update(alertUri, values, null /* where */, null /* args */);
+ cr.update(alertUri, values, null /* where */, null /* args */);
}
-
- ContentResolver cr = getContentResolver();
+
Cursor alertCursor = cr.query(alertUri, ALERT_PROJECTION,
null /* selection */, null, null /* sort order */);
-
+
long alertId, eventId, alarmTime;
int minutes;
String eventName;
@@ -146,9 +169,9 @@
location = alertCursor.getString(ALERT_INDEX_EVENT_LOCATION);
allDay = alertCursor.getInt(ALERT_INDEX_ALL_DAY) != 0;
alarmTime = alertCursor.getLong(ALERT_INDEX_ALARM_TIME);
- declined = alertCursor.getInt(ALERT_INDEX_SELF_ATTENDEE_STATUS) ==
+ declined = alertCursor.getInt(ALERT_INDEX_SELF_ATTENDEE_STATUS) ==
Attendees.ATTENDEE_STATUS_DECLINED;
-
+
// If the event was declined, then mark the alarm DISMISSED,
// otherwise, mark the alarm FIRED.
int newState = CalendarAlerts.FIRED;
@@ -162,7 +185,7 @@
alertCursor.close();
}
}
-
+
// Do not show an alert if the event was declined
if (declined) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
@@ -170,10 +193,10 @@
}
return;
}
-
+
long beginTime = bundle.getLong(Calendar.EVENT_BEGIN_TIME, 0);
long endTime = bundle.getLong(Calendar.EVENT_END_TIME, 0);
-
+
// Check if this alarm is still valid. The time of the event may
// have been changed, or the reminder may have been changed since
// this alarm was set. First, search for an instance in the Instances
@@ -200,7 +223,7 @@
instanceCursor.close();
}
}
-
+
// Check that a reminder for this event exists with the same number
// of minutes. But snoozed alarms have minutes = 0, so don't do this
// check for snoozed alarms.
@@ -224,7 +247,7 @@
}
}
}
-
+
// If the event time was changed and the event has already ended,
// then don't sound the alarm.
if (alarmTime > instanceEnd) {
@@ -253,7 +276,7 @@
}
return;
}
-
+
// Check for another alarm in the CalendarAlerts table that has the
// same event id and the same "minutes". This can occur
// if the event start time was changed to an earlier time and the
@@ -283,11 +306,11 @@
}
}
}
-
+
// Find all the alerts that have fired but have not been dismissed
selection = CalendarAlerts.STATE + "=" + CalendarAlerts.FIRED;
alertCursor = CalendarAlerts.query(cr, ALERT_PROJECTION, selection, null);
-
+
if (alertCursor == null || alertCursor.getCount() == 0) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "no fired alarms found");
@@ -319,13 +342,13 @@
} finally {
alertCursor.close();
}
-
+
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "creating new alarm notification, numReminders: " + numReminders);
}
Notification notification = AlertReceiver.makeNewAlertNotification(this, eventName,
location, numReminders);
-
+
// Generate either a pop-up dialog, status bar notification, or
// neither. Pop-up dialog and status bar notification may include a
// sound, an alert, or both. A status bar notification also includes
@@ -333,30 +356,44 @@
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String reminderType = prefs.getString(CalendarPreferenceActivity.KEY_ALERTS_TYPE,
CalendarPreferenceActivity.ALERT_TYPE_STATUS_BAR);
-
+
if (reminderType.equals(CalendarPreferenceActivity.ALERT_TYPE_OFF)) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "alert preference is OFF");
}
return;
}
-
- NotificationManager nm =
+
+ NotificationManager nm =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- boolean reminderVibrate =
+ boolean reminderVibrate =
prefs.getBoolean(CalendarPreferenceActivity.KEY_ALERTS_VIBRATE, false);
- String reminderRingtone =
- prefs.getString(CalendarPreferenceActivity.KEY_ALERTS_RINGTONE, null);
// Possibly generate a vibration
if (reminderVibrate) {
notification.defaults |= Notification.DEFAULT_VIBRATE;
}
-
- // Possibly generate a sound. If 'Silent' is chosen, the ringtone string will be empty.
- notification.sound = TextUtils.isEmpty(reminderRingtone) ? null : Uri
- .parse(reminderRingtone);
-
+
+ // Temp fix. If we sounded an notification recently, be quiet so the
+ // audio won't overlap.
+
+ // TODO Long term fix: CalendarProvider currently setup an alarm with
+ // AlarmManager for each event notification. So AlertService can post
+ // multiple notifications back to back if there are multiple alarms that
+ // fire at the same time. Instead of doing that, CalendarProvider should
+ // setup one alarm for each wake up time. AlertService can query for
+ // alerts table and update notification manager only once.
+ if (!alarmsFiredRecently) {
+ // Possibly generate a sound. If 'Silent' is chosen, the ringtone
+ // string will be empty.
+ String reminderRingtone = prefs.getString(
+ CalendarPreferenceActivity.KEY_ALERTS_RINGTONE, null);
+ notification.sound = TextUtils.isEmpty(reminderRingtone) ? null : Uri
+ .parse(reminderRingtone);
+ } else {
+ notification.sound = null;
+ }
+
if (reminderType.equals(CalendarPreferenceActivity.ALERT_TYPE_ALERTS)) {
Intent alertIntent = new Intent();
alertIntent.setClass(this, AlertActivity.class);
@@ -366,17 +403,17 @@
LayoutInflater inflater;
inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.alert_toast, null);
-
+
AlertAdapter.updateView(this, view, eventName, location, beginTime, endTime, allDay);
}
-
+
// Record the notify time in the CalendarAlerts table.
// This is used for debugging missed alarms.
ContentValues values = new ContentValues();
long currentTime = System.currentTimeMillis();
values.put(CalendarAlerts.NOTIFY_TIME, currentTime);
cr.update(alertUri, values, null /* where */, null /* args */);
-
+
// The notification time should be pretty close to the reminder time
// that the user set for this event. If the notification is late, then
// that's a bug and we should log an error.
@@ -393,7 +430,7 @@
nm.notify(0, notification);
}
-
+
private void doTimeChanged() {
ContentResolver cr = getContentResolver();
Object service = getSystemService(Context.ALARM_SERVICE);
@@ -401,19 +438,19 @@
CalendarAlerts.rescheduleMissedAlarms(cr, this, manager);
AlertReceiver.updateAlertNotification(this);
}
-
+
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
-
+
@Override
public void handleMessage(Message msg) {
processMessage(msg);
// NOTE: We MUST not call stopSelf() directly, since we need to
// make sure the wake lock acquired by AlertReceiver is released.
AlertReceiver.finishStartingService(AlertService.this, msg.arg1);
- }
+ }
};
@Override
@@ -421,17 +458,20 @@
HandlerThread thread = new HandlerThread("AlertService",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
-
+
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
- public void onStart(Intent intent, int startId) {
- Message msg = mServiceHandler.obtainMessage();
- msg.arg1 = startId;
- msg.obj = intent.getExtras();
- mServiceHandler.sendMessage(msg);
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent != null) {
+ Message msg = mServiceHandler.obtainMessage();
+ msg.arg1 = startId;
+ msg.obj = intent.getExtras();
+ mServiceHandler.sendMessage(msg);
+ }
+ return START_REDELIVER_INTENT;
}
@Override
diff --git a/src/com/android/calendar/CalendarActivity.java b/src/com/android/calendar/CalendarActivity.java
index 2bc991a..b44d519 100644
--- a/src/com/android/calendar/CalendarActivity.java
+++ b/src/com/android/calendar/CalendarActivity.java
@@ -16,9 +16,6 @@
package com.android.calendar;
-import dalvik.system.VMRuntime;
-
-import android.accounts.AccountMonitor;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -41,6 +38,8 @@
import android.widget.ProgressBar;
import android.widget.ViewSwitcher;
+import dalvik.system.VMRuntime;
+
/**
* This is the base class for Day and Week Activities.
*/
@@ -53,8 +52,6 @@
private ContentResolver mContentResolver;
- private AccountMonitor mAccountMonitor;
-
protected ProgressBar mProgressBar;
protected ViewSwitcher mViewSwitcher;
protected Animation mInAnimationForward;
@@ -103,7 +100,7 @@
// Eliminate extra GCs during startup by setting the initial heap size to 4MB.
// TODO: We should restore the old heap size once the activity reaches the idle state
- long oldHeapSize = VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);
+ VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);
setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
mContentResolver = getContentResolver();
@@ -128,6 +125,16 @@
}
@Override
+ protected void onNewIntent(Intent intent) {
+ long timeMillis = Utils.timeFromIntentInMillis(intent);
+ if (timeMillis > 0) {
+ Time time = new Time();
+ time.set(timeMillis);
+ goTo(time, false);
+ }
+ }
+
+ @Override
protected void onResume() {
super.onResume();
mEventLoader.startBackgroundThread();
@@ -153,14 +160,6 @@
}
@Override
- protected void onDestroy() {
- if (mAccountMonitor != null) {
- mAccountMonitor.close();
- }
- super.onDestroy();
- }
-
- @Override
protected void onPause() {
super.onPause();
mContentResolver.unregisterContentObserver(mObserver);
@@ -184,15 +183,16 @@
}
/* Navigator interface methods */
- public void goTo(Time time) {
- CalendarView current = (CalendarView) mViewSwitcher.getCurrentView();
-
- if (current.getSelectedTime().before(time)) {
- mViewSwitcher.setInAnimation(mInAnimationForward);
- mViewSwitcher.setOutAnimation(mOutAnimationForward);
- } else {
- mViewSwitcher.setInAnimation(mInAnimationBackward);
- mViewSwitcher.setOutAnimation(mOutAnimationBackward);
+ public void goTo(Time time, boolean animate) {
+ if (animate) {
+ CalendarView current = (CalendarView) mViewSwitcher.getCurrentView();
+ if (current.getSelectedTime().before(time)) {
+ mViewSwitcher.setInAnimation(mInAnimationForward);
+ mViewSwitcher.setOutAnimation(mOutAnimationForward);
+ } else {
+ mViewSwitcher.setInAnimation(mInAnimationBackward);
+ mViewSwitcher.setOutAnimation(mOutAnimationBackward);
+ }
}
CalendarView next = (CalendarView) mViewSwitcher.getNextView();
@@ -260,7 +260,7 @@
if (progress > 1.0f) {
progress = 1.0f;
}
-
+
float inFromXValue, inToXValue;
float outFromXValue, outToXValue;
if (forward) {
@@ -274,7 +274,7 @@
outFromXValue = progress;
outToXValue = 1.0f;
}
-
+
// We have to allocate these animation objects each time we switch views
// because that is the only way to set the animation parameters.
TranslateAnimation inAnimation = new TranslateAnimation(
@@ -288,14 +288,14 @@
Animation.RELATIVE_TO_SELF, outToXValue,
Animation.ABSOLUTE, 0.0f,
Animation.ABSOLUTE, 0.0f);
-
+
// Reduce the animation duration based on how far we have already swiped.
long duration = (long) (ANIMATION_DURATION * (1.0f - progress));
inAnimation.setDuration(duration);
outAnimation.setDuration(duration);
mViewSwitcher.setInAnimation(inAnimation);
mViewSwitcher.setOutAnimation(outAnimation);
-
+
CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
view.cleanup();
mViewSwitcher.showNext();
@@ -344,12 +344,6 @@
}
@Override
- public void onShowPress(MotionEvent ev) {
- CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
- view.doShowPress(ev);
- }
-
- @Override
public void onLongPress(MotionEvent ev) {
CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
view.doLongPress(ev);
diff --git a/src/com/android/calendar/CalendarApplication.java b/src/com/android/calendar/CalendarApplication.java
index a3dad95..424e422 100644
--- a/src/com/android/calendar/CalendarApplication.java
+++ b/src/com/android/calendar/CalendarApplication.java
@@ -32,7 +32,7 @@
* points to the first element in the list (the most recently visited
* screen).
*/
- /* package */ class Screen {
+ static class Screen {
public int id;
public Screen next;
public Screen previous;
diff --git a/src/com/android/calendar/CalendarView.java b/src/com/android/calendar/CalendarView.java
index 8db673b..6716edf 100644
--- a/src/com/android/calendar/CalendarView.java
+++ b/src/com/android/calendar/CalendarView.java
@@ -74,6 +74,8 @@
public class CalendarView extends View
implements View.OnCreateContextMenuListener, View.OnClickListener {
+ private static float mScale = 0; // Used for supporting different screen densities
+
private boolean mOnFlingCalled;
protected CalendarApplication mCalendarApp;
@@ -82,8 +84,10 @@
private static final String[] CALENDARS_PROJECTION = new String[] {
Calendars._ID, // 0
Calendars.ACCESS_LEVEL, // 1
+ Calendars.OWNER_ACCOUNT, // 2
};
private static final int CALENDARS_INDEX_ACCESS_LEVEL = 1;
+ private static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2;
private static final String CALENDARS_WHERE = Calendars._ID + "=%d";
private static final String[] ATTENDEES_PROJECTION = new String[] {
@@ -93,7 +97,7 @@
private static final int ATTENDEES_INDEX_RELATIONSHIP = 1;
private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=%d";
- private static final float SMALL_ROUND_RADIUS = 3.0F;
+ private static float SMALL_ROUND_RADIUS = 3.0F;
private static final int FROM_NONE = 0;
private static final int FROM_ABOVE = 1;
@@ -101,11 +105,11 @@
private static final int FROM_LEFT = 4;
private static final int FROM_RIGHT = 8;
- private static final int HORIZONTAL_SCROLL_THRESHOLD = 50;
+ private static int HORIZONTAL_SCROLL_THRESHOLD = 50;
private ContinueScroll mContinueScroll = new ContinueScroll();
- private class DayHeader{
+ static private class DayHeader{
int cell;
String dateString;
}
@@ -145,7 +149,6 @@
boolean mSelectionAllDay;
private int mCellWidth;
- private boolean mLaunchNewView;
// Pre-allocate these objects and re-use them
private Rect mRect = new Rect();
@@ -179,10 +182,10 @@
private static final int DAY_GAP = 1;
private static final int HOUR_GAP = 1;
- private static final int SINGLE_ALLDAY_HEIGHT = 20;
- private static final int MAX_ALLDAY_HEIGHT = 72;
- private static final int ALLDAY_TOP_MARGIN = 3;
- private static final int MAX_ALLDAY_EVENT_HEIGHT = 18;
+ private static int SINGLE_ALLDAY_HEIGHT = 20;
+ private static int MAX_ALLDAY_HEIGHT = 72;
+ private static int ALLDAY_TOP_MARGIN = 3;
+ private static int MAX_ALLDAY_EVENT_HEIGHT = 18;
/* The extra space to leave above the text in all-day events */
private static final int ALL_DAY_TEXT_TOP_MARGIN = 0;
@@ -200,13 +203,13 @@
/* package */ static final int MILLIS_PER_HOUR = (3600 * 1000);
/* package */ static final int MILLIS_PER_DAY = MILLIS_PER_HOUR * 24;
- private static final int NORMAL_FONT_SIZE = 12;
- private static final int EVENT_TEXT_FONT_SIZE = 12;
- private static final int HOURS_FONT_SIZE = 12;
- private static final int AMPM_FONT_SIZE = 9;
- private static final int MIN_CELL_WIDTH_FOR_TEXT = 10;
+ private static int NORMAL_FONT_SIZE = 12;
+ private static int EVENT_TEXT_FONT_SIZE = 12;
+ private static int HOURS_FONT_SIZE = 12;
+ private static int AMPM_FONT_SIZE = 9;
+ private static int MIN_CELL_WIDTH_FOR_TEXT = 27;
private static final int MAX_EVENT_TEXT_LEN = 500;
- private static final float MIN_EVENT_HEIGHT = 15.0F; // in pixels
+ private static float MIN_EVENT_HEIGHT = 15.0F; // in pixels
private static int mSelectionColor;
private static int mPressedColor;
@@ -307,9 +310,30 @@
private String mDateRange;
private TextView mTitleTextView;
-
+
public CalendarView(CalendarActivity activity) {
super(activity);
+ if (mScale == 0) {
+ mScale = getContext().getResources().getDisplayMetrics().density;
+ if (mScale != 1) {
+ SINGLE_ALLDAY_HEIGHT *= mScale;
+ MAX_ALLDAY_HEIGHT *= mScale;
+ ALLDAY_TOP_MARGIN *= mScale;
+ MAX_ALLDAY_EVENT_HEIGHT *= mScale;
+
+ NORMAL_FONT_SIZE *= mScale;
+ EVENT_TEXT_FONT_SIZE *= mScale;
+ HOURS_FONT_SIZE *= mScale;
+ AMPM_FONT_SIZE *= mScale;
+ MIN_CELL_WIDTH_FOR_TEXT *= mScale;
+ MIN_EVENT_HEIGHT *= mScale;
+
+ HORIZONTAL_SCROLL_THRESHOLD *= mScale;
+
+ SMALL_ROUND_RADIUS *= mScale;
+ }
+ }
+
mResources = activity.getResources();
mEventLoader = activity.mEventLoader;
mEventGeometry = new EventGeometry();
@@ -812,8 +836,7 @@
} else {
// Switch to the Day/Agenda view.
long millis = getSelectedTimeInMillis();
- MenuHelper.switchTo(mParentActivity, mDetailedView, millis);
- mParentActivity.finish();
+ Utils.startActivity(mParentActivity, mDetailedView, millis);
}
}
} else {
@@ -875,6 +898,13 @@
performLongClick();
}
break;
+ case KeyEvent.KEYCODE_BACK:
+ if (event.isTracking() && !event.isCanceled()) {
+ mPopup.dismiss();
+ mParentActivity.finish();
+ return true;
+ }
+ break;
}
return super.onKeyUp(keyCode, event);
}
@@ -924,9 +954,11 @@
switchViews(true /* trackball or keyboard */);
return true;
case KeyEvent.KEYCODE_BACK:
- mPopup.dismiss();
- mParentActivity.finish();
- return true;
+ if (event.getRepeatCount() == 0) {
+ event.startTracking();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
case KeyEvent.KEYCODE_DPAD_LEFT:
if (mSelectedEvent != null) {
mSelectedEvent = mSelectedEvent.nextLeft;
@@ -2303,21 +2335,10 @@
mTouchMode = TOUCH_MODE_DOWN;
mViewStartX = 0;
mOnFlingCalled = false;
- mLaunchNewView = false;
getHandler().removeCallbacks(mContinueScroll);
}
void doSingleTapUp(MotionEvent ev) {
- mSelectionMode = SELECTION_SELECTED;
- mRedrawScreen = true;
- invalidate();
- if (mLaunchNewView) {
- mLaunchNewView = false;
- switchViews(false /* not the trackball */);
- }
- }
-
- void doShowPress(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
Event selectedEvent = mSelectedEvent;
@@ -2326,29 +2347,41 @@
boolean validPosition = setSelectionFromPosition(x, y);
if (!validPosition) {
+ // return if the touch wasn't on an area of concern
return;
}
- mSelectionMode = SELECTION_PRESSED;
+ mSelectionMode = SELECTION_SELECTED;
mRedrawScreen = true;
invalidate();
- // If the tap is on an already selected event or hour slot,
- // then launch a new view. Otherwise, just select the event.
- if (selectedEvent != null && selectedEvent == mSelectedEvent) {
- // Launch the "View event" view when the finger lifts up,
- // unless the finger moves before lifting up.
- mLaunchNewView = true;
- } else if (selectedEvent == null && selectedDay == mSelectionDay
+ boolean launchNewView = false;
+ if (mSelectedEvent != null) {
+ // If the tap is on an event, launch the "View event" view
+ launchNewView = true;
+ } else if (mSelectedEvent == null && selectedDay == mSelectionDay
&& selectedHour == mSelectionHour) {
- // Launch the Day/Agenda view when the finger lifts up,
- // unless the finger moves before lifting up.
- mLaunchNewView = true;
+ // If the tap is on an already selected hour slot,
+ // then launch the Day/Agenda view. Otherwise, just select the hour
+ // slot.
+ launchNewView = true;
+ }
+
+ if (launchNewView) {
+ switchViews(false /* not the trackball */);
}
}
void doLongPress(MotionEvent ev) {
- mLaunchNewView = false;
+ int x = (int) ev.getX();
+ int y = (int) ev.getY();
+
+ boolean validPosition = setSelectionFromPosition(x, y);
+ if (!validPosition) {
+ // return if the touch wasn't on an area of concern
+ return;
+ }
+
mSelectionMode = SELECTION_LONGPRESS;
mRedrawScreen = true;
invalidate();
@@ -2356,7 +2389,6 @@
}
void doScroll(MotionEvent e1, MotionEvent e2, float deltaX, float deltaY) {
- mLaunchNewView = false;
// Use the distance from the current point to the initial touch instead
// of deltaX and deltaY to avoid accumulating floating-point rounding
// errors. Also, we don't need floats, we can use ints.
@@ -2684,14 +2716,12 @@
}
case MenuHelper.MENU_DAY: {
long startMillis = getSelectedTimeInMillis();
- MenuHelper.switchTo(mParentActivity, DayActivity.class.getName(), startMillis);
- mParentActivity.finish();
+ Utils.startActivity(mParentActivity, DayActivity.class.getName(), startMillis);
break;
}
case MenuHelper.MENU_AGENDA: {
long startMillis = getSelectedTimeInMillis();
- MenuHelper.switchTo(mParentActivity, AgendaActivity.class.getName(), startMillis);
- mParentActivity.finish();
+ Utils.startActivity(mParentActivity, AgendaActivity.class.getName(), startMillis);
break;
}
case MenuHelper.MENU_EVENT_CREATE: {
@@ -2746,25 +2776,23 @@
String where = String.format(CALENDARS_WHERE, calId);
cursor = cr.query(uri, CALENDARS_PROJECTION, where, null, null);
+ String calendarOwnerAccount = null;
if (cursor != null) {
cursor.moveToFirst();
visibility = cursor.getInt(CALENDARS_INDEX_ACCESS_LEVEL);
+ calendarOwnerAccount = cursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
cursor.close();
}
-
- // Attendees cursor
- uri = Attendees.CONTENT_URI;
- where = String.format(ATTENDEES_WHERE, e.id);
- Cursor attendeesCursor = cr.query(uri, ATTENDEES_PROJECTION, where, null, null);
- if (attendeesCursor != null) {
- if (attendeesCursor.moveToFirst()) {
- relationship = attendeesCursor.getInt(ATTENDEES_INDEX_RELATIONSHIP);
- }
- attendeesCursor.close();
+
+ if (visibility < Calendars.CONTRIBUTOR_ACCESS) {
+ return false;
}
- return visibility >= Calendars.CONTRIBUTOR_ACCESS &&
- relationship >= Attendees.RELATIONSHIP_ORGANIZER;
+ if (e.guestsCanModify) {
+ return true;
+ }
+
+ return !TextUtils.isEmpty(calendarOwnerAccount) && calendarOwnerAccount.equals(e.organizer);
}
/**
diff --git a/src/com/android/calendar/DayActivity.java b/src/com/android/calendar/DayActivity.java
index 51f965b..e2bf14c 100644
--- a/src/com/android/calendar/DayActivity.java
+++ b/src/com/android/calendar/DayActivity.java
@@ -16,9 +16,7 @@
package com.android.calendar;
-import android.content.SharedPreferences;
import android.os.Bundle;
-import android.preference.PreferenceManager;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ProgressBar;
@@ -42,16 +40,6 @@
mViewSwitcher.setFactory(this);
mViewSwitcher.getCurrentView().requestFocus();
mProgressBar = (ProgressBar) findViewById(R.id.progress_circular);
-
- // Record Day View as the (new) default detailed view.
- String activityString = CalendarApplication.ACTIVITY_NAMES[CalendarApplication.DAY_VIEW_ID];
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(CalendarPreferenceActivity.KEY_DETAILED_VIEW, activityString);
-
- // Record Day View as the (new) start view
- editor.putString(CalendarPreferenceActivity.KEY_START_VIEW, activityString);
- editor.commit();
}
public View makeView() {
@@ -68,5 +56,8 @@
super.onPause();
CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
mSelectedDay = view.getSelectedDay();
+
+ // Record Day View as the (new) default detailed view.
+ Utils.setDefaultView(this, CalendarApplication.DAY_VIEW_ID);
}
}
diff --git a/src/com/android/calendar/DeleteEventHelper.java b/src/com/android/calendar/DeleteEventHelper.java
index d0693fa..bf27a9e 100644
--- a/src/com/android/calendar/DeleteEventHelper.java
+++ b/src/com/android/calendar/DeleteEventHelper.java
@@ -52,8 +52,6 @@
* {@link #delete()} multiple times).
*/
public class DeleteEventHelper {
-
- private static final String TAG = "DeleteEventHelper";
private final Activity mParent;
private final ContentResolver mContentResolver;
diff --git a/src/com/android/calendar/EditEvent.java b/src/com/android/calendar/EditEvent.java
index ce545fa..9438419 100644
--- a/src/com/android/calendar/EditEvent.java
+++ b/src/com/android/calendar/EditEvent.java
@@ -18,6 +18,11 @@
import static android.provider.Calendar.EVENT_BEGIN_TIME;
import static android.provider.Calendar.EVENT_END_TIME;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
@@ -26,30 +31,41 @@
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.content.AsyncQueryHandler;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.OperationApplicationException;
import android.content.SharedPreferences;
+import android.content.ContentProviderOperation.Builder;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.os.RemoteException;
import android.pim.EventRecurrence;
import android.preference.PreferenceManager;
+import android.provider.Calendar.Attendees;
import android.provider.Calendar.Calendars;
import android.provider.Calendar.Events;
import android.provider.Calendar.Reminders;
+import android.text.Editable;
+import android.text.InputFilter;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.text.format.Time;
+import android.text.util.Rfc822InputFilter;
+import android.text.util.Rfc822Token;
+import android.text.util.Rfc822Tokenizer;
+import android.text.util.Rfc822Validator;
import android.util.Log;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -62,19 +78,28 @@
import android.widget.DatePicker;
import android.widget.ImageButton;
import android.widget.LinearLayout;
+import android.widget.MultiAutoCompleteTextView;
import android.widget.ResourceCursorAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;
+import com.google.android.googlelogin.GoogleLoginServiceConstants;
+
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.TimeZone;
public class EditEvent extends Activity implements View.OnClickListener,
DialogInterface.OnCancelListener, DialogInterface.OnClickListener {
+ private static final String TAG = "EditEvent";
+ private static final boolean DEBUG = false;
+
/**
* This is the symbolic name for the key used to pass in the boolean
* for creating all-day events that is part of the extra data of the intent.
@@ -94,20 +119,22 @@
private static final int MENU_HIDE_EXTRA_OPTIONS = 3;
private static final String[] EVENT_PROJECTION = new String[] {
- Events._ID, // 0
- Events.TITLE, // 1
- Events.DESCRIPTION, // 2
- Events.EVENT_LOCATION, // 3
- Events.ALL_DAY, // 4
- Events.HAS_ALARM, // 5
- Events.CALENDAR_ID, // 6
- Events.DTSTART, // 7
- Events.DURATION, // 8
- Events.EVENT_TIMEZONE, // 9
- Events.RRULE, // 10
- Events._SYNC_ID, // 11
- Events.TRANSPARENCY, // 12
- Events.VISIBILITY, // 13
+ Events._ID, // 0
+ Events.TITLE, // 1
+ Events.DESCRIPTION, // 2
+ Events.EVENT_LOCATION, // 3
+ Events.ALL_DAY, // 4
+ Events.HAS_ALARM, // 5
+ Events.CALENDAR_ID, // 6
+ Events.DTSTART, // 7
+ Events.DURATION, // 8
+ Events.EVENT_TIMEZONE, // 9
+ Events.RRULE, // 10
+ Events._SYNC_ID, // 11
+ Events.TRANSPARENCY, // 12
+ Events.VISIBILITY, // 13
+ Events.OWNER_ACCOUNT, // 14
+ Events.HAS_ATTENDEE_DATA, // 15
};
private static final int EVENT_INDEX_ID = 0;
private static final int EVENT_INDEX_TITLE = 1;
@@ -123,14 +150,16 @@
private static final int EVENT_INDEX_SYNC_ID = 11;
private static final int EVENT_INDEX_TRANSPARENCY = 12;
private static final int EVENT_INDEX_VISIBILITY = 13;
+ private static final int EVENT_INDEX_OWNER_ACCOUNT = 14;
+ private static final int EVENT_INDEX_HAS_ATTENDEE_DATA = 15;
private static final String[] CALENDARS_PROJECTION = new String[] {
- Calendars._ID, // 0
- Calendars.DISPLAY_NAME, // 1
- Calendars.TIMEZONE, // 2
+ Calendars._ID, // 0
+ Calendars.DISPLAY_NAME, // 1
+ Calendars.OWNER_ACCOUNT, // 2
};
private static final int CALENDARS_INDEX_DISPLAY_NAME = 1;
- private static final int CALENDARS_INDEX_TIMEZONE = 2;
+ private static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2;
private static final String CALENDARS_WHERE = Calendars.ACCESS_LEVEL + ">=" +
Calendars.CONTRIBUTOR_ACCESS + " AND " + Calendars.SYNC_EVENTS + "=1";
@@ -143,6 +172,17 @@
Reminders.METHOD + "=" + Reminders.METHOD_ALERT + " OR " + Reminders.METHOD + "=" +
Reminders.METHOD_DEFAULT + ")";
+ private static final String[] ATTENDEES_PROJECTION = new String[] {
+ Attendees.ATTENDEE_NAME, // 0
+ Attendees.ATTENDEE_EMAIL, // 1
+ };
+ private static final int ATTENDEES_INDEX_NAME = 0;
+ private static final int ATTENDEES_INDEX_EMAIL = 1;
+ private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=? AND "
+ + Attendees.ATTENDEE_RELATIONSHIP + "<>" + Attendees.RELATIONSHIP_ORGANIZER;
+ private static final String ATTENDEES_DELETE_PREFIX = Attendees.EVENT_ID + "=? AND " +
+ Attendees.ATTENDEE_EMAIL + " IN (";
+
private static final int DOES_NOT_REPEAT = 0;
private static final int REPEATS_DAILY = 1;
private static final int REPEATS_EVERY_WEEKDAY = 2;
@@ -156,7 +196,7 @@
private static final int MODIFY_SELECTED = 1;
private static final int MODIFY_ALL = 2;
private static final int MODIFY_ALL_FOLLOWING = 3;
-
+
private static final int DAY_IN_SECONDS = 24 * 60 * 60;
private int mFirstDayOfWeek; // cached in onCreate
@@ -184,6 +224,13 @@
private LinearLayout mExtraOptions;
private ArrayList<Integer> mOriginalMinutes = new ArrayList<Integer>();
private ArrayList<LinearLayout> mReminderItems = new ArrayList<LinearLayout>(0);
+ private Rfc822Validator mEmailValidator;
+ private MultiAutoCompleteTextView mAttendeesList;
+ private EmailAddressAdapter mAddressAdapter;
+ private String mOriginalAttendees = "";
+
+ // Used to control the visibility of the Guests textview. Default to true
+ private boolean mHasAttendeeData = true;
private EventRecurrence mEventRecurrence = new EventRecurrence();
private String mRrule;
@@ -192,6 +239,7 @@
private ProgressDialog mLoadingCalendarsDialog;
private AlertDialog mNoCalendarsDialog;
private ContentValues mInitialValues;
+ private String mOwnerAccount;
/**
* If the repeating event is created on the phone and it hasn't been
@@ -214,7 +262,8 @@
private DeleteEventHelper mDeleteEventHelper;
private QueryHandler mQueryHandler;
-
+ private AccountManager mAccountManager;
+
/* This class is used to update the time buttons. */
private class TimeListener implements OnTimeSetListener {
private View mView;
@@ -350,7 +399,7 @@
}
}
- private class CalendarsAdapter extends ResourceCursorAdapter {
+ static private class CalendarsAdapter extends ResourceCursorAdapter {
public CalendarsAdapter(Context context, Cursor c) {
super(context, R.layout.calendars_item, c);
setDropDownViewResource(R.layout.calendars_dropdown_item);
@@ -373,7 +422,7 @@
}
return;
}
-
+
if (v == mDeleteButton) {
long begin = mStartTime.toMillis(false /* use isDst */);
long end = mEndTime.toMillis(false /* use isDst */);
@@ -392,12 +441,12 @@
mDeleteEventHelper.delete(begin, end, mEventCursor, which);
return;
}
-
+
if (v == mDiscardButton) {
finish();
return;
}
-
+
// This must be a click on one of the "remove reminder" buttons
LinearLayout reminderItem = (LinearLayout) v.getParent();
LinearLayout parent = (LinearLayout) reminderItem.getParent();
@@ -426,7 +475,7 @@
finish();
}
}
-
+
private class QueryHandler extends AsyncQueryHandler {
public QueryHandler(ContentResolver cr) {
super(cr);
@@ -442,7 +491,7 @@
} else {
mCalendarsCursor = cursor;
startManagingCursor(cursor);
-
+
// Stop the spinner
getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
Window.PROGRESS_VISIBILITY_OFF);
@@ -454,7 +503,7 @@
if (mSaveAfterQueryComplete) {
mLoadingCalendarsDialog.cancel();
}
-
+
// Create an error message for the user that, when clicked,
// will exit this activity without saving the event.
AlertDialog.Builder builder = new AlertDialog.Builder(EditEvent.this);
@@ -467,17 +516,87 @@
return;
}
+ int primaryCalendarPosition = findPrimaryCalendarPosition();
+
// populate the calendars spinner
CalendarsAdapter adapter = new CalendarsAdapter(EditEvent.this, mCalendarsCursor);
mCalendarsSpinner.setAdapter(adapter);
+ mCalendarsSpinner.setSelection(primaryCalendarPosition);
mCalendarsQueryComplete = true;
if (mSaveAfterQueryComplete) {
mLoadingCalendarsDialog.cancel();
save();
finish();
}
+
+ // Find user domain and set it to the validator.
+ // TODO: we may want to update this validator if the user actually picks
+ // a different calendar. maybe not. depends on what we want for the
+ // user experience. this may change when we add support for multiple
+ // accounts, anyway.
+ if (mHasAttendeeData && cursor.moveToPosition(primaryCalendarPosition)) {
+ String ownEmail = cursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
+ if (ownEmail != null) {
+ String domain = extractDomain(ownEmail);
+ if (domain != null) {
+ mEmailValidator = new Rfc822Validator(domain);
+ mAttendeesList.setValidator(mEmailValidator);
+ }
+ }
+ }
}
}
+
+ // Find the calendar position in the cursor that matches the signed-in
+ // account
+ private int findPrimaryCalendarPosition() {
+ int primaryCalendarPosition = -1;
+ try {
+ Account[] accounts = mAccountManager.getAccountsByTypeAndFeatures(
+ GoogleLoginServiceConstants.ACCOUNT_TYPE, new String[] {
+ GoogleLoginServiceConstants.FEATURE_LEGACY_HOSTED_OR_GOOGLE
+ }, null, null).getResult();
+ if (accounts.length > 0) {
+ for (int i = 0; i < accounts.length && primaryCalendarPosition == -1; ++i) {
+ String name = accounts[i].name;
+ if (name == null) {
+ continue;
+ }
+
+ int position = 0;
+ mCalendarsCursor.moveToPosition(-1);
+ while (mCalendarsCursor.moveToNext()) {
+ if (name.equals(mCalendarsCursor
+ .getString(CALENDARS_INDEX_OWNER_ACCOUNT))) {
+ primaryCalendarPosition = position;
+ break;
+ }
+ position++;
+ }
+ }
+ }
+ } catch (OperationCanceledException e) {
+ Log.w(TAG, "Ignoring unexpected exception", e);
+ } catch (IOException e) {
+ Log.w(TAG, "Ignoring unexpected exception", e);
+ } catch (AuthenticatorException e) {
+ Log.w(TAG, "Ignoring unexpected exception", e);
+ } finally {
+ if (primaryCalendarPosition != -1) {
+ return primaryCalendarPosition;
+ } else {
+ return 0;
+ }
+ }
+ }
+ }
+
+ private static String extractDomain(String email) {
+ int separator = email.lastIndexOf('@');
+ if (separator != -1 && ++separator < email.length()) {
+ return email.substring(separator);
+ }
+ return null;
}
@Override
@@ -485,6 +604,9 @@
super.onCreate(icicle);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.edit_event);
+ mAccountManager = AccountManager.get(this);
+
+ boolean newEvent = false;
mFirstDayOfWeek = Calendar.getInstance().getFirstDayOfWeek();
@@ -506,15 +628,25 @@
long begin = intent.getLongExtra(EVENT_BEGIN_TIME, 0);
long end = intent.getLongExtra(EVENT_END_TIME, 0);
+ String domain = "gmail.com";
+
boolean allDay = false;
if (mEventCursor != null) {
// The event already exists so fetch the all-day status
mEventCursor.moveToFirst();
+ mHasAttendeeData = mEventCursor.getInt(EVENT_INDEX_HAS_ATTENDEE_DATA) != 0;
allDay = mEventCursor.getInt(EVENT_INDEX_ALL_DAY) != 0;
String rrule = mEventCursor.getString(EVENT_INDEX_RRULE);
String timezone = mEventCursor.getString(EVENT_INDEX_TIMEZONE);
long calendarId = mEventCursor.getInt(EVENT_INDEX_CALENDAR_ID);
-
+ mOwnerAccount = mEventCursor.getString(EVENT_INDEX_OWNER_ACCOUNT);
+ if (!TextUtils.isEmpty(mOwnerAccount)) {
+ String ownerDomain = extractDomain(mOwnerAccount);
+ if (ownerDomain != null) {
+ domain = ownerDomain;
+ }
+ }
+
// Remember the initial values
mInitialValues = new ContentValues();
mInitialValues.put(EVENT_BEGIN_TIME, begin);
@@ -524,10 +656,11 @@
mInitialValues.put(Events.EVENT_TIMEZONE, timezone);
mInitialValues.put(Events.CALENDAR_ID, calendarId);
} else {
+ newEvent = true;
// We are creating a new event, so set the default from the
// intent (if specified).
allDay = intent.getBooleanExtra(EVENT_ALL_DAY, false);
-
+
// Start the spinner
getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
Window.PROGRESS_VISIBILITY_ON);
@@ -584,6 +717,14 @@
mRemindersContainer = (LinearLayout) findViewById(R.id.reminder_items_container);
mExtraOptions = (LinearLayout) findViewById(R.id.extra_options_container);
+ if (mHasAttendeeData) {
+ mAddressAdapter = new EmailAddressAdapter(this);
+ mEmailValidator = new Rfc822Validator(domain);
+ mAttendeesList = initMultiAutoCompleteTextView(R.id.attendees);
+ } else {
+ findViewById(R.id.attendees_group).setVisibility(View.GONE);
+ }
+
mAllDayCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
@@ -648,14 +789,20 @@
prefs.getString(CalendarPreferenceActivity.KEY_DEFAULT_REMINDER, "0");
mDefaultReminderMinutes = Integer.parseInt(durationString);
+ if (newEvent && mDefaultReminderMinutes != 0) {
+ addReminder(this, this, mReminderItems, mReminderValues,
+ mReminderLabels, mDefaultReminderMinutes);
+ }
+
+ long eventId = (mEventCursor == null) ? -1 : mEventCursor.getLong(EVENT_INDEX_ID);
+ ContentResolver cr = getContentResolver();
+
// Reminders cursor
boolean hasAlarm = (mEventCursor != null)
&& (mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) != 0);
if (hasAlarm) {
Uri uri = Reminders.CONTENT_URI;
- long eventId = mEventCursor.getLong(EVENT_INDEX_ID);
String where = String.format(REMINDERS_WHERE, eventId);
- ContentResolver cr = getContentResolver();
Cursor reminderCursor = cr.query(uri, REMINDERS_PROJECTION, where, null, null);
try {
// First pass: collect all the custom reminder minutes (e.g.,
@@ -664,7 +811,7 @@
int minutes = reminderCursor.getInt(REMINDERS_INDEX_MINUTES);
EditEvent.addMinutesToList(this, mReminderValues, mReminderLabels, minutes);
}
-
+
// Second pass: create the reminder spinners
reminderCursor.moveToPosition(-1);
while (reminderCursor.moveToNext()) {
@@ -684,45 +831,109 @@
public void onClick(View v) {
addReminder();
}
- };
+ };
ImageButton reminderRemoveButton = (ImageButton) findViewById(R.id.reminder_add);
reminderRemoveButton.setOnClickListener(addReminderOnClickListener);
mDeleteEventHelper = new DeleteEventHelper(this, true /* exit when done */);
+ // Attendees cursor
+ if (mHasAttendeeData && eventId != -1) {
+ Uri uri = Attendees.CONTENT_URI;
+ String[] whereArgs = {Long.toString(eventId)};
+ Cursor attendeeCursor = cr.query(uri, ATTENDEES_PROJECTION, ATTENDEES_WHERE, whereArgs,
+ null);
+ try {
+ StringBuilder b = new StringBuilder();
+ while (attendeeCursor.moveToNext()) {
+ String name = attendeeCursor.getString(ATTENDEES_INDEX_NAME);
+ String email = attendeeCursor.getString(ATTENDEES_INDEX_EMAIL);
+ if (email != null) {
+ if (name != null && name.length() > 0 && !name.equals(email)) {
+ b.append('"').append(name).append("\" ");
+ }
+ b.append('<').append(email).append(">, ");
+ }
+ }
+ if (b.length() > 0) {
+ mOriginalAttendees = b.toString();
+ mAttendeesList.setText(mOriginalAttendees);
+ }
+ } finally {
+ attendeeCursor.close();
+ }
+ }
if (mEventCursor == null) {
// Allow the intent to specify the fields in the event.
// This will allow other apps to create events easily.
initFromIntent(intent);
}
}
-
+
+ private LinkedHashSet<Rfc822Token> getAddressesFromList(MultiAutoCompleteTextView list) {
+ list.clearComposingText();
+ LinkedHashSet<Rfc822Token> addresses = new LinkedHashSet<Rfc822Token>();
+ Rfc822Tokenizer.tokenize(list.getText(), addresses);
+
+ // validate the emails, out of paranoia. they should already be
+ // validated on input, but drop any invalid emails just to be safe.
+ for (Rfc822Token address : addresses) {
+ if (!mEmailValidator.isValid(address.getAddress())) {
+ Log.w(TAG, "Dropping invalid attendee email address: " + address);
+ addresses.remove(address);
+ }
+ }
+ return addresses;
+ }
+
+ // From com.google.android.gm.ComposeActivity
+ private MultiAutoCompleteTextView initMultiAutoCompleteTextView(int res) {
+ MultiAutoCompleteTextView list = (MultiAutoCompleteTextView) findViewById(res);
+ list.setAdapter(mAddressAdapter);
+ list.setTokenizer(new Rfc822Tokenizer());
+ list.setValidator(mEmailValidator);
+
+ // NOTE: assumes no other filters are set
+ list.setFilters(sRecipientFilters);
+
+ return list;
+ }
+
+ /**
+ * From com.google.android.gm.ComposeActivity
+ * Implements special address cleanup rules:
+ * The first space key entry following an "@" symbol that is followed by any combination
+ * of letters and symbols, including one+ dots and zero commas, should insert an extra
+ * comma (followed by the space).
+ */
+ private static InputFilter[] sRecipientFilters = new InputFilter[] { new Rfc822InputFilter() };
+
private void initFromIntent(Intent intent) {
String title = intent.getStringExtra(Events.TITLE);
if (title != null) {
mTitleTextView.setText(title);
}
-
+
String location = intent.getStringExtra(Events.EVENT_LOCATION);
if (location != null) {
mLocationTextView.setText(location);
}
-
+
String description = intent.getStringExtra(Events.DESCRIPTION);
if (description != null) {
mDescriptionTextView.setText(description);
}
-
+
int availability = intent.getIntExtra(Events.TRANSPARENCY, -1);
if (availability != -1) {
mAvailabilitySpinner.setSelection(availability);
}
-
+
int visibility = intent.getIntExtra(Events.VISIBILITY, -1);
if (visibility != -1) {
mVisibilitySpinner.setSelection(visibility);
}
-
+
String rrule = intent.getStringExtra(Events.RRULE);
if (rrule != null) {
mRrule = rrule;
@@ -741,7 +952,7 @@
return;
}
}
-
+
if (mEventCursor != null) {
Cursor cursor = mEventCursor;
cursor.moveToFirst();
@@ -795,7 +1006,7 @@
} else if (which == 2) {
mModification = MODIFY_ALL_FOLLOWING;
}
-
+
// If we are modifying all the events in a
// series then disable and ignore the date.
if (mModification == MODIFY_ALL) {
@@ -819,26 +1030,23 @@
// since we can't change the calendar.
View calendarGroup = findViewById(R.id.calendar_group);
calendarGroup.setVisibility(View.GONE);
- } else if (Time.isEpoch(mStartTime) && Time.isEpoch(mEndTime)) {
- mStartTime.setToNow();
-
- // Round the time to the nearest half hour.
- mStartTime.second = 0;
- int minute = mStartTime.minute;
- if (minute > 0 && minute <= 30) {
- mStartTime.minute = 30;
- } else {
- mStartTime.minute = 0;
- mStartTime.hour += 1;
- }
-
- long startMillis = mStartTime.normalize(true /* ignore isDst */);
- mEndTime.set(startMillis + DateUtils.HOUR_IN_MILLIS);
} else {
- // New event - set the default reminder
- if (mDefaultReminderMinutes != 0) {
- addReminder(this, this, mReminderItems, mReminderValues,
- mReminderLabels, mDefaultReminderMinutes);
+ // New event
+ if (Time.isEpoch(mStartTime) && Time.isEpoch(mEndTime)) {
+ mStartTime.setToNow();
+
+ // Round the time to the nearest half hour.
+ mStartTime.second = 0;
+ int minute = mStartTime.minute;
+ if (minute > 0 && minute <= 30) {
+ mStartTime.minute = 30;
+ } else {
+ mStartTime.minute = 0;
+ mStartTime.hour += 1;
+ }
+
+ long startMillis = mStartTime.normalize(true /* ignore isDst */);
+ mEndTime.set(startMillis + DateUtils.HOUR_IN_MILLIS);
}
// Hide delete button
@@ -919,23 +1127,18 @@
}
@Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_BACK:
- // If we are creating a new event, do not create it if the
- // title, location and description are all empty, in order to
- // prevent accidental "no subject" event creations.
- if (mUri != null || !isEmpty()) {
- if (!save()) {
- // We cannot exit this activity because the calendars
- // are still loading.
- return true;
- }
- }
- break;
+ public void onBackPressed() {
+ // If we are creating a new event, do not create it if the
+ // title, location and description are all empty, in order to
+ // prevent accidental "no subject" event creations.
+ if (mUri != null || !isEmpty()) {
+ if (!save()) {
+ // We cannot exit this activity because the calendars
+ // are still loading.
+ return;
+ }
}
-
- return super.onKeyDown(keyCode, event);
+ finish();
}
private void populateWhen() {
@@ -1069,7 +1272,7 @@
LinearLayout parent = (LinearLayout) activity.findViewById(R.id.reminder_items_container);
LinearLayout reminderItem = (LinearLayout) inflater.inflate(R.layout.edit_reminder_item, null);
parent.addView(reminderItem);
-
+
Spinner spinner = (Spinner) reminderItem.findViewById(R.id.reminder_value);
Resources res = activity.getResources();
spinner.setPrompt(res.getString(R.string.reminders_label));
@@ -1077,7 +1280,7 @@
ArrayAdapter<String> adapter = new ArrayAdapter<String>(activity, resource, labels);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
-
+
ImageButton reminderRemoveButton;
reminderRemoveButton = (ImageButton) reminderItem.findViewById(R.id.reminder_remove);
reminderRemoveButton.setOnClickListener(listener);
@@ -1088,17 +1291,17 @@
return true;
}
-
+
static void addMinutesToList(Context context, ArrayList<Integer> values,
ArrayList<String> labels, int minutes) {
int index = values.indexOf(minutes);
if (index != -1) {
return;
}
-
+
// The requested "minutes" does not exist in the list, so insert it
// into the list.
-
+
String label = constructReminderLabel(context, minutes, false);
int len = values.size();
for (int i = 0; i < len; i++) {
@@ -1108,14 +1311,14 @@
return;
}
}
-
+
values.add(minutes);
labels.add(len, label);
}
-
+
/**
* Finds the index of the given "minutes" in the "values" list.
- *
+ *
* @param values the list of minutes corresponding to the spinner choices
* @param minutes the minutes to search for in the values list
* @return the index of "minutes" in the "values" list
@@ -1129,7 +1332,7 @@
}
return index;
}
-
+
// Constructs a label given an arbitrary number of minutes. For example,
// if the given minutes is 63, then this returns the string "63 minutes".
// As another example, if the given minutes is 120, then this returns
@@ -1137,7 +1340,7 @@
static String constructReminderLabel(Context context, int minutes, boolean abbrev) {
Resources resources = context.getResources();
int value, resId;
-
+
if (minutes % 60 != 0) {
value = minutes;
if (abbrev) {
@@ -1185,7 +1388,7 @@
// Saves the event. Returns true if it is okay to exit this activity.
private boolean save() {
boolean forceSaveReminders = false;
-
+
// If we are creating a new event, then make sure we wait until the
// query to fetch the list of calendars has finished.
if (mEventCursor == null) {
@@ -1215,30 +1418,42 @@
Toast.makeText(this, R.string.saving_event, Toast.LENGTH_SHORT).show();
}
- ContentResolver cr = getContentResolver();
+ ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+ int eventIdIndex = -1;
+
ContentValues values = getContentValuesFromUi();
Uri uri = mUri;
+ // Update the "hasAlarm" field for the event
+ ArrayList<Integer> reminderMinutes = reminderItemsToMinutes(mReminderItems,
+ mReminderValues);
+ int len = reminderMinutes.size();
+ values.put(Events.HAS_ALARM, (len > 0) ? 1 : 0);
+
// For recurring events, we must make sure that we use duration rather
// than dtend.
if (uri == null) {
+ // Add hasAttendeeData for a new event
+ values.put(Events.HAS_ATTENDEE_DATA, 1);
// Create new event with new contents
addRecurrenceRule(values);
- uri = cr.insert(Events.CONTENT_URI, values);
+ eventIdIndex = ops.size();
+ Builder b = ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values);
+ ops.add(b.build());
forceSaveReminders = true;
} else if (mRrule == null) {
// Modify contents of a non-repeating event
addRecurrenceRule(values);
checkTimeDependentFields(values);
- cr.update(uri, values, null, null);
-
+ ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
+
} else if (mInitialValues.getAsString(Events.RRULE) == null) {
// This event was changed from a non-repeating event to a
// repeating event.
addRecurrenceRule(values);
values.remove(Events.DTEND);
- cr.update(uri, values, null, null);
+ ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
} else if (mModification == MODIFY_SELECTED) {
// Modify contents of the current instance of repeating event
@@ -1250,7 +1465,9 @@
boolean allDay = mInitialValues.getAsInteger(Events.ALL_DAY) != 0;
values.put(Events.ORIGINAL_ALL_DAY, allDay ? 1 : 0);
- uri = cr.insert(Events.CONTENT_URI, values);
+ eventIdIndex = ops.size();
+ Builder b = ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values);
+ ops.add(b.build());
forceSaveReminders = true;
} else if (mModification == MODIFY_ALL_FOLLOWING) {
@@ -1263,56 +1480,185 @@
// then delete the whole series. Otherwise, update the series
// to end at the new start time.
if (isFirstEventInSeries()) {
- cr.delete(uri, null, null);
+ ops.add(ContentProviderOperation.newDelete(uri).build());
} else {
// Update the current repeating event to end at the new
// start time.
- updatePastEvents(cr, uri);
+ updatePastEvents(ops, uri);
}
- uri = cr.insert(Events.CONTENT_URI, values);
+ eventIdIndex = ops.size();
+ ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values)
+ .build());
} else {
if (isFirstEventInSeries()) {
checkTimeDependentFields(values);
values.remove(Events.DTEND);
- cr.update(uri, values, null, null);
+ Builder b = ContentProviderOperation.newUpdate(uri).withValues(values);
+ ops.add(b.build());
} else {
// Update the current repeating event to end at the new
// start time.
- updatePastEvents(cr, uri);
+ updatePastEvents(ops, uri);
// Create a new event with the user-modified fields
values.remove(Events.DTEND);
- uri = cr.insert(Events.CONTENT_URI, values);
+ eventIdIndex = ops.size();
+ ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(
+ values).build());
}
}
forceSaveReminders = true;
} else if (mModification == MODIFY_ALL) {
-
+
// Modify all instances of repeating event
addRecurrenceRule(values);
-
+
if (mRrule == null) {
// We've changed a recurring event to a non-recurring event.
// Delete the whole series and replace it with a new
// non-recurring event.
- cr.delete(uri, null, null);
- uri = cr.insert(Events.CONTENT_URI, values);
+ ops.add(ContentProviderOperation.newDelete(uri).build());
+
+ eventIdIndex = ops.size();
+ ops.add(ContentProviderOperation.newInsert(Events.CONTENT_URI).withValues(values)
+ .build());
forceSaveReminders = true;
} else {
checkTimeDependentFields(values);
values.remove(Events.DTEND);
- cr.update(uri, values, null, null);
+ ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
}
}
- if (uri != null) {
+ // New Event or New Exception to an existing event
+ boolean newEvent = (eventIdIndex != -1);
+
+ if (newEvent) {
+ saveRemindersWithBackRef(ops, eventIdIndex, reminderMinutes, mOriginalMinutes,
+ forceSaveReminders);
+ } else if (uri != null) {
long eventId = ContentUris.parseId(uri);
- ArrayList<Integer> reminderMinutes = reminderItemsToMinutes(mReminderItems,
- mReminderValues);
- saveReminders(cr, eventId, reminderMinutes, mOriginalMinutes,
+ saveReminders(ops, eventId, reminderMinutes, mOriginalMinutes,
forceSaveReminders);
}
+
+ Builder b;
+
+ // New event/instance - Set Organizer's response as yes
+ if (mHasAttendeeData && newEvent) {
+ values.clear();
+ int calendarCursorPosition = mCalendarsSpinner.getSelectedItemPosition();
+ String ownerEmail = mOwnerAccount;
+ // Just in case mOwnerAccount is null, try to get owner from mCalendarsCursor
+ if (ownerEmail == null && mCalendarsCursor != null &&
+ mCalendarsCursor.moveToPosition(calendarCursorPosition)) {
+ ownerEmail = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
+ }
+ if (ownerEmail != null) {
+ values.put(Attendees.ATTENDEE_EMAIL, ownerEmail);
+ values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ORGANIZER);
+ values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE);
+ values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_ACCEPTED);
+
+ b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI)
+ .withValues(values);
+ b.withValueBackReference(Reminders.EVENT_ID, eventIdIndex);
+ ops.add(b.build());
+ }
+ }
+
+ // TODO: is this the right test? this currently checks if this is
+ // a new event or an existing event. or is this a paranoia check?
+ if (mHasAttendeeData && (newEvent || uri != null)) {
+ Editable attendeesText = mAttendeesList.getText();
+ // Hit the content provider only if this is a new event or the user has changed it
+ if (newEvent || !mOriginalAttendees.equals(attendeesText.toString())) {
+ // figure out which attendees need to be added and which ones
+ // need to be deleted. use a linked hash set, so we maintain
+ // order (but also remove duplicates).
+ LinkedHashSet<Rfc822Token> newAttendees = getAddressesFromList(mAttendeesList);
+
+ // the eventId is only used if eventIdIndex is -1.
+ // TODO: clean up this code.
+ long eventId = uri != null ? ContentUris.parseId(uri) : -1;
+
+ // only compute deltas if this is an existing event.
+ // new events (being inserted into the Events table) won't
+ // have any existing attendees.
+ if (!newEvent) {
+ HashSet<Rfc822Token> removedAttendees = new HashSet<Rfc822Token>();
+ HashSet<Rfc822Token> originalAttendees = new HashSet<Rfc822Token>();
+ Rfc822Tokenizer.tokenize(mOriginalAttendees, originalAttendees);
+ for (Rfc822Token originalAttendee : originalAttendees) {
+ if (newAttendees.contains(originalAttendee)) {
+ // existing attendee. remove from new attendees set.
+ newAttendees.remove(originalAttendee);
+ } else {
+ // no longer in attendees. mark as removed.
+ removedAttendees.add(originalAttendee);
+ }
+ }
+
+ // delete removed attendees
+ b = ContentProviderOperation.newDelete(Attendees.CONTENT_URI);
+
+ String[] args = new String[removedAttendees.size() + 1];
+ args[0] = Long.toString(eventId);
+ int i = 1;
+ StringBuilder deleteWhere = new StringBuilder(ATTENDEES_DELETE_PREFIX);
+ for (Rfc822Token removedAttendee : removedAttendees) {
+ if (i > 1) {
+ deleteWhere.append(",");
+ }
+ deleteWhere.append("?");
+ args[i++] = removedAttendee.getAddress();
+ }
+ deleteWhere.append(")");
+ b.withSelection(deleteWhere.toString(), args);
+ ops.add(b.build());
+ }
+
+ if (newAttendees.size() > 0) {
+ // Insert the new attendees
+ for (Rfc822Token attendee : newAttendees) {
+ values.clear();
+ values.put(Attendees.ATTENDEE_NAME, attendee.getName());
+ values.put(Attendees.ATTENDEE_EMAIL, attendee.getAddress());
+ values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
+ values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE);
+ values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE);
+
+ if (newEvent) {
+ b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI)
+ .withValues(values);
+ b.withValueBackReference(Attendees.EVENT_ID, eventIdIndex);
+ } else {
+ values.put(Attendees.EVENT_ID, eventId);
+ b = ContentProviderOperation.newInsert(Attendees.CONTENT_URI)
+ .withValues(values);
+ }
+ ops.add(b.build());
+ }
+ }
+ }
+ }
+
+ try {
+ // TODO Move this to background thread
+ ContentProviderResult[] results =
+ getContentResolver().applyBatch(android.provider.Calendar.AUTHORITY, ops);
+ if (DEBUG) {
+ for (int i = 0; i < results.length; i++) {
+ Log.v(TAG, "results = " + results[i].toString());
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Ignoring unexpected remote exception", e);
+ } catch (OperationApplicationException e) {
+ Log.w(TAG, "Ignoring unexpected exception", e);
+ }
+
return true;
}
@@ -1322,7 +1668,7 @@
return start == mStartTime.toMillis(true);
}
- private void updatePastEvents(ContentResolver cr, Uri uri) {
+ private void updatePastEvents(ArrayList<ContentProviderOperation> ops, Uri uri) {
long oldStartMillis = mEventCursor.getLong(EVENT_INDEX_DTSTART);
String oldDuration = mEventCursor.getString(EVENT_INDEX_DURATION);
boolean allDay = mEventCursor.getInt(EVENT_INDEX_ALL_DAY) != 0;
@@ -1338,17 +1684,17 @@
// must include just the date field, and not the time field. The
// repeating events repeat up to and including the "until" time.
untilTime.timezone = Time.TIMEZONE_UTC;
-
+
// Subtract one second from the old begin time to get the new
// "until" time.
- untilTime.set(begin - 1000); // subtract one second (1000 millis)
+ untilTime.set(begin - 1000); // subtract one second (1000 millis)
if (allDay) {
untilTime.hour = 0;
untilTime.minute = 0;
untilTime.second = 0;
untilTime.allDay = true;
untilTime.normalize(false);
-
+
// For all-day events, the duration must be in days, not seconds.
// Otherwise, Google Calendar will (mistakenly) change this event
// into a non-all-day event.
@@ -1364,7 +1710,8 @@
oldValues.put(Events.DTSTART, oldStartMillis);
oldValues.put(Events.DURATION, oldDuration);
oldValues.put(Events.RRULE, mEventRecurrence.toString());
- cr.update(uri, oldValues, null, null);
+ Builder b = ContentProviderOperation.newUpdate(uri).withValues(oldValues);
+ ops.add(b.build());
}
private void checkTimeDependentFields(ContentValues values) {
@@ -1373,13 +1720,13 @@
boolean oldAllDay = mInitialValues.getAsInteger(Events.ALL_DAY) != 0;
String oldRrule = mInitialValues.getAsString(Events.RRULE);
String oldTimezone = mInitialValues.getAsString(Events.EVENT_TIMEZONE);
-
+
long newBegin = values.getAsLong(Events.DTSTART);
long newEnd = values.getAsLong(Events.DTEND);
boolean newAllDay = values.getAsInteger(Events.ALL_DAY) != 0;
String newRrule = values.getAsString(Events.RRULE);
String newTimezone = values.getAsString(Events.EVENT_TIMEZONE);
-
+
// If none of the time-dependent fields changed, then remove them.
if (oldBegin == newBegin && oldEnd == newEnd && oldAllDay == newAllDay
&& TextUtils.equals(oldRrule, newRrule)
@@ -1414,7 +1761,7 @@
values.put(Events.DTSTART, oldStartMillis);
}
}
-
+
static ArrayList<Integer> reminderItemsToMinutes(ArrayList<LinearLayout> reminderItems,
ArrayList<Integer> reminderValues) {
int len = reminderItems.size();
@@ -1431,8 +1778,8 @@
/**
* Saves the reminders, if they changed. Returns true if the database
* was updated.
- *
- * @param cr the ContentResolver
+ *
+ * @param ops the array of ContentProviderOperations
* @param eventId the id of the event whose reminders are being updated
* @param reminderMinutes the array of reminders set by the user
* @param originalMinutes the original array of reminders
@@ -1440,7 +1787,7 @@
* change
* @return true if the database was updated
*/
- static boolean saveReminders(ContentResolver cr, long eventId,
+ static boolean saveReminders(ArrayList<ContentProviderOperation> ops, long eventId,
ArrayList<Integer> reminderMinutes, ArrayList<Integer> originalMinutes,
boolean forceSave) {
// If the reminders have not changed, then don't update the database
@@ -1451,14 +1798,12 @@
// Delete all the existing reminders for this event
String where = Reminders.EVENT_ID + "=?";
String[] args = new String[] { Long.toString(eventId) };
- cr.delete(Reminders.CONTENT_URI, where, args);
+ Builder b = ContentProviderOperation.newDelete(Reminders.CONTENT_URI);
+ b.withSelection(where, args);
+ ops.add(b.build());
- // Update the "hasAlarm" field for the event
ContentValues values = new ContentValues();
int len = reminderMinutes.size();
- values.put(Events.HAS_ALARM, (len > 0) ? 1 : 0);
- Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
- cr.update(uri, values, null /* where */, null /* selection args */);
// Insert the new reminders, if any
for (int i = 0; i < len; i++) {
@@ -1468,7 +1813,39 @@
values.put(Reminders.MINUTES, minutes);
values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
values.put(Reminders.EVENT_ID, eventId);
- cr.insert(Reminders.CONTENT_URI, values);
+ b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(values);
+ ops.add(b.build());
+ }
+ return true;
+ }
+
+ static boolean saveRemindersWithBackRef(ArrayList<ContentProviderOperation> ops,
+ int eventIdIndex, ArrayList<Integer> reminderMinutes,
+ ArrayList<Integer> originalMinutes, boolean forceSave) {
+ // If the reminders have not changed, then don't update the database
+ if (reminderMinutes.equals(originalMinutes) && !forceSave) {
+ return false;
+ }
+
+ // Delete all the existing reminders for this event
+ Builder b = ContentProviderOperation.newDelete(Reminders.CONTENT_URI);
+ b.withSelection(Reminders.EVENT_ID + "=?", new String[1]);
+ b.withSelectionBackReference(0, eventIdIndex);
+ ops.add(b.build());
+
+ ContentValues values = new ContentValues();
+ int len = reminderMinutes.size();
+
+ // Insert the new reminders, if any
+ for (int i = 0; i < len; i++) {
+ int minutes = reminderMinutes.get(i);
+
+ values.clear();
+ values.put(Reminders.MINUTES, minutes);
+ values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
+ b = ContentProviderOperation.newInsert(Reminders.CONTENT_URI).withValues(values);
+ b.withValueBackReference(Reminders.EVENT_ID, eventIdIndex);
+ ops.add(b.build());
}
return true;
}
@@ -1479,7 +1856,7 @@
if (mRrule == null) {
return;
}
-
+
values.put(Events.RRULE, mRrule);
long end = mEndTime.toMillis(true /* ignore dst */);
long start = mStartTime.toMillis(true /* ignore dst */);
@@ -1599,7 +1976,7 @@
mEndTime.monthDay++;
mEndTime.timezone = timezone;
endMillis = mEndTime.normalize(true);
-
+
if (mEventCursor == null) {
// This is a new event
calendarId = mCalendarsSpinner.getSelectedItemId();
@@ -1612,7 +1989,7 @@
if (mEventCursor != null) {
// This is an existing event
timezone = mEventCursor.getString(EVENT_INDEX_TIMEZONE);
-
+
// The timezone might be null if we are changing an existing
// all-day event to a non-all-day event. We need to assign
// a timezone to the non-all-day event.
@@ -1623,7 +2000,7 @@
} else {
// This is a new event
calendarId = mCalendarsSpinner.getSelectedItemId();
-
+
// The timezone for a new event is the currently displayed
// timezone, NOT the timezone of the containing calendar.
timezone = TimeZone.getDefault().getID();
diff --git a/src/com/android/calendar/EmailAddressAdapter.java b/src/com/android/calendar/EmailAddressAdapter.java
new file mode 100644
index 0000000..bfcb986
--- /dev/null
+++ b/src/com/android/calendar/EmailAddressAdapter.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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 the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.calendar;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.text.util.Rfc822Token;
+import android.view.View;
+import android.widget.ResourceCursorAdapter;
+import android.widget.TextView;
+
+// Customized from com.android.email.EmailAddressAdapter
+
+public class EmailAddressAdapter extends ResourceCursorAdapter {
+ public static final int NAME_INDEX = 1;
+ public static final int DATA_INDEX = 2;
+
+ private static final String SORT_ORDER =
+ Contacts.TIMES_CONTACTED + " DESC, " + Contacts.DISPLAY_NAME;
+
+ private ContentResolver mContentResolver;
+
+ private static final String[] PROJECTION = {
+ Data._ID, // 0
+ Contacts.DISPLAY_NAME, // 1
+ Email.DATA // 2
+ };
+
+ public EmailAddressAdapter(Context context) {
+ super(context, android.R.layout.simple_dropdown_item_1line, null);
+ mContentResolver = context.getContentResolver();
+ }
+
+ @Override
+ public final String convertToString(Cursor cursor) {
+ return makeDisplayString(cursor);
+ }
+
+ private final String makeDisplayString(Cursor cursor) {
+ String name = cursor.getString(NAME_INDEX);
+ String address = cursor.getString(DATA_INDEX);
+
+ return new Rfc822Token(name, address, null).toString();
+ }
+
+ @Override
+ public final void bindView(View view, Context context, Cursor cursor) {
+ ((TextView) view).setText(makeDisplayString(cursor));
+ }
+
+ @Override
+ public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
+ String filter = constraint == null ? "" : constraint.toString();
+ Uri uri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, Uri.encode(filter));
+ return mContentResolver.query(uri, PROJECTION, null, null, SORT_ORDER);
+ }
+}
diff --git a/src/com/android/calendar/Event.java b/src/com/android/calendar/Event.java
index c15e5b5..956a0f6 100644
--- a/src/com/android/calendar/Event.java
+++ b/src/com/android/calendar/Event.java
@@ -23,6 +23,7 @@
import android.os.Debug;
import android.preference.PreferenceManager;
import android.provider.Calendar.Attendees;
+import android.provider.Calendar.Events;
import android.provider.Calendar.Instances;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -56,6 +57,8 @@
Instances.RRULE, // 14
Instances.RDATE, // 15
Instances.SELF_ATTENDEE_STATUS, // 16
+ Events.ORGANIZER, // 17
+ Events.GUESTS_CAN_MODIFY, // 18
};
// The indices for the projection array above.
@@ -75,12 +78,16 @@
private static final int PROJECTION_RRULE_INDEX = 14;
private static final int PROJECTION_RDATE_INDEX = 15;
private static final int PROJECTION_SELF_ATTENDEE_STATUS_INDEX = 16;
+ private static final int PROJECTION_ORGANIZER_INDEX = 17;
+ private static final int PROJECTION_GUESTS_CAN_INVITE_OTHERS_INDEX = 18;
public long id;
public int color;
public CharSequence title;
public CharSequence location;
public boolean allDay;
+ public String organizer;
+ public boolean guestsCanModify;
public int startDay; // start Julian day
public int endDay; // end Julian day
@@ -113,7 +120,8 @@
private static final int MIDNIGHT_IN_MINUTES = 24 * 60;
@Override
- public final Object clone() {
+ public final Object clone() throws CloneNotSupportedException {
+ super.clone();
Event e = new Event();
e.title = title;
@@ -129,6 +137,8 @@
e.hasAlarm = hasAlarm;
e.isRepeating = isRepeating;
e.selfAttendeeStatus = selfAttendeeStatus;
+ e.organizer = organizer;
+ e.guestsCanModify = guestsCanModify;
return e;
}
@@ -148,6 +158,8 @@
dest.hasAlarm = hasAlarm;
dest.isRepeating = isRepeating;
dest.selfAttendeeStatus = selfAttendeeStatus;
+ dest.organizer = organizer;
+ dest.guestsCanModify = guestsCanModify;
}
public static final Event newInstance() {
@@ -195,6 +207,9 @@
if (allDay && !e.allDay) return -1;
if (!allDay && e.allDay) return 1;
+ if (guestsCanModify && !e.guestsCanModify) return -1;
+ if (!guestsCanModify && e.guestsCanModify) return 1;
+
// If two events have the same time range, then sort them in
// alphabetical order based on their titles.
int cmp = compareStrings(title, e.title);
@@ -209,6 +224,11 @@
if (cmp != 0) {
return cmp;
}
+
+ cmp = compareStrings(organizer, e.organizer);
+ if (cmp != 0) {
+ return cmp;
+ }
return 0;
}
@@ -315,6 +335,9 @@
e.title = c.getString(PROJECTION_TITLE_INDEX);
e.location = c.getString(PROJECTION_LOCATION_INDEX);
e.allDay = c.getInt(PROJECTION_ALL_DAY_INDEX) != 0;
+ e.organizer = c.getString(PROJECTION_ORGANIZER_INDEX);
+ e.guestsCanModify = c.getInt(PROJECTION_GUESTS_CAN_INVITE_OTHERS_INDEX) != 0;
+
String timezone = c.getString(PROJECTION_TIMEZONE_INDEX);
if (e.title == null || e.title.length() == 0) {
@@ -537,6 +560,8 @@
Log.e("Cal", "+ endDay = " + endDay);
Log.e("Cal", "+ startTime = " + startTime);
Log.e("Cal", "+ endTime = " + endTime);
+ Log.e("Cal", "+ organizer = " + organizer);
+ Log.e("Cal", "+ guestwrt = " + guestsCanModify);
}
public final boolean intersects(int julianDay, int startMinute,
diff --git a/src/com/android/calendar/EventInfoActivity.java b/src/com/android/calendar/EventInfoActivity.java
index 9be1900..1abcb0b 100644
--- a/src/com/android/calendar/EventInfoActivity.java
+++ b/src/com/android/calendar/EventInfoActivity.java
@@ -18,36 +18,61 @@
import static android.provider.Calendar.EVENT_BEGIN_TIME;
import static android.provider.Calendar.EVENT_END_TIME;
+import static android.provider.Calendar.AttendeesColumns.ATTENDEE_STATUS;
+
import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.AsyncQueryHandler;
+import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
+import android.content.OperationApplicationException;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.PorterDuff;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.pim.ContactsAsyncHelper;
import android.pim.EventRecurrence;
import android.preference.PreferenceManager;
import android.provider.Calendar;
+import android.provider.ContactsContract;
import android.provider.Calendar.Attendees;
import android.provider.Calendar.Calendars;
import android.provider.Calendar.Events;
import android.provider.Calendar.Reminders;
+import android.provider.ContactsContract.CommonDataKinds;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.QuickContact;
+import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.Presence;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.text.util.Linkify;
+import android.text.util.Rfc822Token;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnTouchListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.QuickContactBadge;
import android.widget.ImageButton;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
@@ -55,11 +80,16 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.TimeZone;
import java.util.regex.Pattern;
public class EventInfoActivity extends Activity implements View.OnClickListener,
AdapterView.OnItemSelectedListener {
+ public static final boolean DEBUG = false;
+
+ public static final String TAG = "EventInfoActivity";
+
private static final int MAX_REMINDERS = 5;
/**
@@ -83,6 +113,11 @@
Events.HAS_ALARM, // 10
Events.ACCESS_LEVEL, // 11
Events.COLOR, // 12
+ Events.HAS_ATTENDEE_DATA, // 13
+ Events.GUESTS_CAN_MODIFY, // 14
+ // TODO Events.GUESTS_CAN_INVITE_OTHERS has not been implemented in calendar provider
+ Events.GUESTS_CAN_INVITE_OTHERS, // 15
+ Events.ORGANIZER, // 16
};
private static final int EVENT_INDEX_ID = 0;
private static final int EVENT_INDEX_TITLE = 1;
@@ -96,22 +131,37 @@
private static final int EVENT_INDEX_HAS_ALARM = 10;
private static final int EVENT_INDEX_ACCESS_LEVEL = 11;
private static final int EVENT_INDEX_COLOR = 12;
+ private static final int EVENT_INDEX_HAS_ATTENDEE_DATA = 13;
+ private static final int EVENT_INDEX_GUESTS_CAN_MODIFY = 14;
+ private static final int EVENT_INDEX_CAN_INVITE_OTHERS = 15;
+ private static final int EVENT_INDEX_ORGANIZER = 16;
private static final String[] ATTENDEES_PROJECTION = new String[] {
Attendees._ID, // 0
- Attendees.ATTENDEE_RELATIONSHIP, // 1
- Attendees.ATTENDEE_STATUS, // 2
+ Attendees.ATTENDEE_NAME, // 1
+ Attendees.ATTENDEE_EMAIL, // 2
+ Attendees.ATTENDEE_RELATIONSHIP, // 3
+ Attendees.ATTENDEE_STATUS, // 4
};
private static final int ATTENDEES_INDEX_ID = 0;
- private static final int ATTENDEES_INDEX_RELATIONSHIP = 1;
- private static final int ATTENDEES_INDEX_STATUS = 2;
+ private static final int ATTENDEES_INDEX_NAME = 1;
+ private static final int ATTENDEES_INDEX_EMAIL = 2;
+ private static final int ATTENDEES_INDEX_RELATIONSHIP = 3;
+ private static final int ATTENDEES_INDEX_STATUS = 4;
+
private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=%d";
+ private static final String ATTENDEES_SORT_ORDER = Attendees.ATTENDEE_NAME + " ASC, "
+ + Attendees.ATTENDEE_EMAIL + " ASC";
+
static final String[] CALENDARS_PROJECTION = new String[] {
- Calendars._ID, // 0
- Calendars.DISPLAY_NAME, // 1
+ Calendars._ID, // 0
+ Calendars.DISPLAY_NAME, // 1
+ Calendars.OWNER_ACCOUNT, // 2
};
static final int CALENDARS_INDEX_DISPLAY_NAME = 1;
+ static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2;
+
static final String CALENDARS_WHERE = Calendars._ID + "=%d";
private static final String[] REMINDERS_PROJECTION = new String[] {
@@ -122,6 +172,7 @@
private static final String REMINDERS_WHERE = Reminders.EVENT_ID + "=%d AND (" +
Reminders.METHOD + "=" + Reminders.METHOD_ALERT + " OR " + Reminders.METHOD + "=" +
Reminders.METHOD_DEFAULT + ")";
+ private static final String REMINDERS_SORT = Reminders.MINUTES;
private static final int MENU_GROUP_REMINDER = 1;
private static final int MENU_GROUP_EDIT = 2;
@@ -140,6 +191,8 @@
};
private LinearLayout mRemindersContainer;
+ private LinearLayout mOrganizerContainer;
+ private TextView mOrganizerView;
private Uri mUri;
private long mEventId;
@@ -149,8 +202,16 @@
private long mStartMillis;
private long mEndMillis;
- private int mVisibility = Calendars.NO_ACCESS;
- private int mRelationship = Attendees.RELATIONSHIP_ORGANIZER;
+
+ private boolean mHasAttendeeData;
+ private boolean mIsOrganizer;
+ private long mCalendarOwnerAttendeeId = -1;
+ private String mCalendarOwnerAccount;
+ private boolean mCanModifyCalendar;
+ private boolean mIsBusyFreeCalendar;
+ private boolean mCanModifyEvent;
+ private int mNumOfAttendees;
+ private String mOrganizer;
private ArrayList<Integer> mOriginalMinutes = new ArrayList<Integer>();
private ArrayList<LinearLayout> mReminderItems = new ArrayList<LinearLayout>(0);
@@ -163,9 +224,41 @@
private int mResponseOffset;
private int mOriginalAttendeeResponse;
+ private int mAttendeeResponseFromIntent = ATTENDEE_NO_RESPONSE;
private boolean mIsRepeating;
private Pattern mWildcardPattern = Pattern.compile("^.*$");
+ private LayoutInflater mLayoutInflater;
+ private LinearLayout mReminderAdder;
+
+ // TODO This can be removed when the contacts content provider doesn't return duplicates
+ private int mUpdateCounts;
+ private static class ViewHolder {
+ QuickContactBadge badge;
+ ImageView presence;
+ int updateCounts;
+ }
+ private HashMap<String, ViewHolder> mViewHolders = new HashMap<String, ViewHolder>();
+ private PresenceQueryHandler mPresenceQueryHandler;
+
+ private static final Uri CONTACT_DATA_WITH_PRESENCE_URI = Data.CONTENT_URI;
+
+ int PRESENCE_PROJECTION_CONTACT_ID_INDEX = 0;
+ int PRESENCE_PROJECTION_PRESENCE_INDEX = 1;
+ int PRESENCE_PROJECTION_EMAIL_INDEX = 2;
+ int PRESENCE_PROJECTION_PHOTO_ID_INDEX = 3;
+
+ private static final String[] PRESENCE_PROJECTION = new String[] {
+ Email.CONTACT_ID, // 0
+ Email.CONTACT_PRESENCE, // 1
+ Email.DATA, // 2
+ Email.PHOTO_ID, // 3
+ };
+
+ ArrayList<Attendee> mAcceptedAttendees = new ArrayList<Attendee>();
+ ArrayList<Attendee> mDeclinedAttendees = new ArrayList<Attendee>();
+ ArrayList<Attendee> mTentativeAttendees = new ArrayList<Attendee>();
+ private int mColor;
// This is called when one of the "remove reminder" buttons is selected.
public void onClick(View v) {
@@ -175,33 +268,33 @@
mReminderItems.remove(reminderItem);
updateRemindersVisibility();
}
-
- public void onItemSelected(AdapterView parent, View v, int position, long id) {
+
+ public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
// If they selected the "No response" option, then don't display the
// dialog asking which events to change.
if (id == 0 && mResponseOffset == 0) {
return;
}
-
+
// If this is not a repeating event, then don't display the dialog
// asking which events to change.
if (!mIsRepeating) {
return;
}
-
+
// If the selection is the same as the original, then don't display the
// dialog asking which events to change.
int index = findResponseIndexFor(mOriginalAttendeeResponse);
if (position == index + mResponseOffset) {
return;
}
-
+
// This is a repeating event. We need to ask the user if they mean to
// change just this one instance or all instances.
mEditResponseHelper.showDialog(mEditResponseHelper.getWhichEvents());
}
- public void onNothingSelected(AdapterView parent) {
+ public void onNothingSelected(AdapterView<?> parent) {
}
@Override
@@ -214,6 +307,7 @@
ContentResolver cr = getContentResolver();
mStartMillis = intent.getLongExtra(EVENT_BEGIN_TIME, 0);
mEndMillis = intent.getLongExtra(EVENT_END_TIME, 0);
+ mAttendeeResponseFromIntent = intent.getIntExtra(ATTENDEE_STATUS, ATTENDEE_NO_RESPONSE);
mEventCursor = managedQuery(mUri, EVENT_PROJECTION, null, null);
if (initEventCursor()) {
// The cursor is empty. This can happen if the event was deleted.
@@ -222,27 +316,41 @@
}
setContentView(R.layout.event_info_activity);
-
- // Attendees cursor
- Uri uri = Attendees.CONTENT_URI;
- String where = String.format(ATTENDEES_WHERE, mEventId);
- mAttendeesCursor = managedQuery(uri, ATTENDEES_PROJECTION, where, null);
- initAttendeesCursor();
+ mPresenceQueryHandler = new PresenceQueryHandler(this, cr);
+ mLayoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mRemindersContainer = (LinearLayout) findViewById(R.id.reminders_container);
+ mOrganizerContainer = (LinearLayout) findViewById(R.id.organizer_container);
+ mOrganizerView = (TextView) findViewById(R.id.organizer);
// Calendars cursor
- uri = Calendars.CONTENT_URI;
- where = String.format(CALENDARS_WHERE, mEventCursor.getLong(EVENT_INDEX_CALENDAR_ID));
+ Uri uri = Calendars.CONTENT_URI;
+ String where = String.format(CALENDARS_WHERE, mEventCursor.getLong(EVENT_INDEX_CALENDAR_ID));
mCalendarsCursor = managedQuery(uri, CALENDARS_PROJECTION, where, null);
- initCalendarsCursor();
-
- Resources res = getResources();
-
- if (mVisibility >= Calendars.CONTRIBUTOR_ACCESS &&
- mRelationship == Attendees.RELATIONSHIP_ATTENDEE) {
- setTitle(res.getString(R.string.event_info_title_invite));
- } else {
- setTitle(res.getString(R.string.event_info_title));
+ mCalendarOwnerAccount = "";
+ if (mCalendarsCursor != null) {
+ mCalendarsCursor.moveToFirst();
+ mCalendarOwnerAccount = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
}
+ String eventOrganizer = mEventCursor.getString(EVENT_INDEX_ORGANIZER);
+ mIsOrganizer = mCalendarOwnerAccount.equals(eventOrganizer);
+ mHasAttendeeData = mEventCursor.getInt(EVENT_INDEX_HAS_ATTENDEE_DATA) != 0;
+
+ updateView();
+
+ // Attendees cursor
+ uri = Attendees.CONTENT_URI;
+ where = String.format(ATTENDEES_WHERE, mEventId);
+ mAttendeesCursor = managedQuery(uri, ATTENDEES_PROJECTION, where, ATTENDEES_SORT_ORDER);
+ initAttendeesCursor();
+
+ mOrganizer = eventOrganizer;
+ mCanModifyCalendar =
+ mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) >= Calendars.CONTRIBUTOR_ACCESS;
+ mIsBusyFreeCalendar =
+ mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) == Calendars.FREEBUSY_ACCESS;
+
+ mCanModifyEvent = mCanModifyCalendar
+ && (mIsOrganizer || (mEventCursor.getInt(EVENT_INDEX_GUESTS_CAN_MODIFY) != 0));
// Initialize the reminder values array.
Resources r = getResources();
@@ -261,14 +369,13 @@
prefs.getString(CalendarPreferenceActivity.KEY_DEFAULT_REMINDER, "0");
mDefaultReminderMinutes = Integer.parseInt(durationString);
- mRemindersContainer = (LinearLayout) findViewById(R.id.reminder_items_container);
-
// Reminders cursor
boolean hasAlarm = mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) != 0;
if (hasAlarm) {
uri = Reminders.CONTENT_URI;
where = String.format(REMINDERS_WHERE, mEventId);
- Cursor reminderCursor = cr.query(uri, REMINDERS_PROJECTION, where, null, null);
+ Cursor reminderCursor = cr.query(uri, REMINDERS_PROJECTION, where, null,
+ REMINDERS_SORT);
try {
// First pass: collect all the custom reminder minutes (e.g.,
// a reminder of 8 minutes) into a global list.
@@ -276,7 +383,7 @@
int minutes = reminderCursor.getInt(REMINDERS_INDEX_MINUTES);
EditEvent.addMinutesToList(this, mReminderValues, mReminderLabels, minutes);
}
-
+
// Second pass: create the reminder spinners
reminderCursor.moveToPosition(-1);
while (reminderCursor.moveToNext()) {
@@ -290,17 +397,17 @@
}
}
- updateView();
- updateRemindersVisibility();
-
// Setup the + Add Reminder Button
View.OnClickListener addReminderOnClickListener = new View.OnClickListener() {
public void onClick(View v) {
addReminder();
}
- };
- ImageButton reminderRemoveButton = (ImageButton) findViewById(R.id.reminder_add);
- reminderRemoveButton.setOnClickListener(addReminderOnClickListener);
+ };
+ ImageButton reminderAddButton = (ImageButton) findViewById(R.id.reminder_add);
+ reminderAddButton.setOnClickListener(addReminderOnClickListener);
+
+ mReminderAdder = (LinearLayout) findViewById(R.id.reminder_adder);
+ updateRemindersVisibility();
mDeleteEventHelper = new DeleteEventHelper(this, true /* exit when done */);
mEditResponseHelper = new EditResponseHelper(this);
@@ -314,8 +421,18 @@
finish();
return;
}
- initAttendeesCursor();
initCalendarsCursor();
+ updateResponse();
+ updateTitle();
+ }
+
+ private void updateTitle() {
+ Resources res = getResources();
+ if (mCanModifyCalendar && !mIsOrganizer) {
+ setTitle(res.getString(R.string.event_info_title_invite));
+ } else {
+ setTitle(res.getString(R.string.event_info_title));
+ }
}
/**
@@ -328,19 +445,81 @@
return true;
}
mEventCursor.moveToFirst();
- mVisibility = mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL);
mEventId = mEventCursor.getInt(EVENT_INDEX_ID);
String rRule = mEventCursor.getString(EVENT_INDEX_RRULE);
mIsRepeating = (rRule != null);
return false;
}
+ private static class Attendee {
+ String mName;
+ String mEmail;
+
+ Attendee(String name, String email) {
+ mName = name;
+ mEmail = email;
+ }
+ }
+
private void initAttendeesCursor() {
+ mOriginalAttendeeResponse = ATTENDEE_NO_RESPONSE;
+ mCalendarOwnerAttendeeId = -1;
+ mNumOfAttendees = 0;
if (mAttendeesCursor != null) {
+ mNumOfAttendees = mAttendeesCursor.getCount();
if (mAttendeesCursor.moveToFirst()) {
- mRelationship = mAttendeesCursor.getInt(ATTENDEES_INDEX_RELATIONSHIP);
+ mAcceptedAttendees.clear();
+ mDeclinedAttendees.clear();
+ mTentativeAttendees.clear();
+
+ do {
+ int status = mAttendeesCursor.getInt(ATTENDEES_INDEX_STATUS);
+ String name = mAttendeesCursor.getString(ATTENDEES_INDEX_NAME);
+ String email = mAttendeesCursor.getString(ATTENDEES_INDEX_EMAIL);
+
+ if (mAttendeesCursor.getInt(ATTENDEES_INDEX_RELATIONSHIP) ==
+ Attendees.RELATIONSHIP_ORGANIZER) {
+ // Overwrites the one from Event table if available
+ if (name != null && name.length() > 0) {
+ mOrganizer = name;
+ } else if (email != null && email.length() > 0) {
+ mOrganizer = email;
+ }
+ }
+
+ if (mCalendarOwnerAttendeeId == -1 && mCalendarOwnerAccount.equals(email)) {
+ mCalendarOwnerAttendeeId = mAttendeesCursor.getInt(ATTENDEES_INDEX_ID);
+ mOriginalAttendeeResponse = mAttendeesCursor.getInt(ATTENDEES_INDEX_STATUS);
+ } else {
+ // Don't show your own status in the list because:
+ // 1) it doesn't make sense for event without other guests.
+ // 2) there's a spinner for that for events with guests.
+ switch(status) {
+ case Attendees.ATTENDEE_STATUS_ACCEPTED:
+ mAcceptedAttendees.add(new Attendee(name, email));
+ break;
+ case Attendees.ATTENDEE_STATUS_DECLINED:
+ mDeclinedAttendees.add(new Attendee(name, email));
+ break;
+ default:
+ mTentativeAttendees.add(new Attendee(name, email));
+ }
+ }
+ } while (mAttendeesCursor.moveToNext());
+ mAttendeesCursor.moveToFirst();
+
+ updateAttendees();
}
}
+ // only show the organizer if we're not the organizer and if
+ // we have attendee data (might have been removed by the server
+ // for events with a lot of attendees).
+ if (!mIsOrganizer && mHasAttendeeData) {
+ mOrganizerContainer.setVisibility(View.VISIBLE);
+ mOrganizerView.setText(mOrganizer);
+ } else {
+ mOrganizerContainer.setVisibility(View.GONE);
+ }
}
private void initCalendarsCursor() {
@@ -358,8 +537,17 @@
ContentResolver cr = getContentResolver();
ArrayList<Integer> reminderMinutes = EditEvent.reminderItemsToMinutes(mReminderItems,
mReminderValues);
- boolean changed = EditEvent.saveReminders(cr, mEventId, reminderMinutes, mOriginalMinutes,
+ ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(3);
+ boolean changed = EditEvent.saveReminders(ops, mEventId, reminderMinutes, mOriginalMinutes,
false /* no force save */);
+ try {
+ cr.applyBatch(Calendars.CONTENT_URI.getAuthority(), ops);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Ignoring exception: ", e);
+ } catch (OperationApplicationException e) {
+ Log.w(TAG, "Ignoring exception: ", e);
+ }
+
changed |= saveResponse(cr);
if (changed) {
Toast.makeText(this, R.string.saving_event, Toast.LENGTH_SHORT).show();
@@ -386,32 +574,22 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- // Cannot add reminders to a shared calendar with only free/busy
- // permissions
- if (mVisibility >= Calendars.READ_ACCESS && mReminderItems.size() < MAX_REMINDERS) {
- menu.setGroupVisible(MENU_GROUP_REMINDER, true);
- menu.setGroupEnabled(MENU_GROUP_REMINDER, true);
- } else {
- menu.setGroupVisible(MENU_GROUP_REMINDER, false);
- menu.setGroupEnabled(MENU_GROUP_REMINDER, false);
- }
+ boolean canAddReminders = canAddReminders();
+ menu.setGroupVisible(MENU_GROUP_REMINDER, canAddReminders);
+ menu.setGroupEnabled(MENU_GROUP_REMINDER, canAddReminders);
- if (mVisibility >= Calendars.CONTRIBUTOR_ACCESS &&
- mRelationship >= Attendees.RELATIONSHIP_ORGANIZER) {
- menu.setGroupVisible(MENU_GROUP_EDIT, true);
- menu.setGroupEnabled(MENU_GROUP_EDIT, true);
- menu.setGroupVisible(MENU_GROUP_DELETE, true);
- menu.setGroupEnabled(MENU_GROUP_DELETE, true);
- } else {
- menu.setGroupVisible(MENU_GROUP_EDIT, false);
- menu.setGroupEnabled(MENU_GROUP_EDIT, false);
- menu.setGroupVisible(MENU_GROUP_DELETE, false);
- menu.setGroupEnabled(MENU_GROUP_DELETE, false);
- }
+ menu.setGroupVisible(MENU_GROUP_EDIT, mCanModifyEvent);
+ menu.setGroupEnabled(MENU_GROUP_EDIT, mCanModifyEvent);
+ menu.setGroupVisible(MENU_GROUP_DELETE, mCanModifyCalendar);
+ menu.setGroupEnabled(MENU_GROUP_DELETE, mCanModifyCalendar);
return super.onPrepareOptionsMenu(menu);
}
-
+
+ private boolean canAddReminders() {
+ return !mIsBusyFreeCalendar && mReminderItems.size() < MAX_REMINDERS;
+ }
+
private void addReminder() {
// TODO: when adding a new reminder, make it different from the
// last one in the list (if any).
@@ -452,17 +630,18 @@
}
private void updateRemindersVisibility() {
- if (mReminderItems.size() == 0) {
+ if (mIsBusyFreeCalendar) {
mRemindersContainer.setVisibility(View.GONE);
} else {
mRemindersContainer.setVisibility(View.VISIBLE);
+ mReminderAdder.setVisibility(canAddReminders() ? View.VISIBLE : View.GONE);
}
}
/**
* Saves the response to an invitation if the user changed the response.
* Returns true if the database was updated.
- *
+ *
* @param cr the ContentResolver
* @return true if the database was changed
*/
@@ -483,10 +662,9 @@
return false;
}
- long attendeeId = mAttendeesCursor.getInt(ATTENDEES_INDEX_ID);
if (!mIsRepeating) {
// This is a non-repeating event
- updateResponse(cr, mEventId, attendeeId, status);
+ updateResponse(cr, mEventId, mCalendarOwnerAttendeeId, status);
return true;
}
@@ -496,30 +674,33 @@
case -1:
return false;
case UPDATE_SINGLE:
- createExceptionResponse(cr, mEventId, attendeeId, status);
+ createExceptionResponse(cr, mEventId, mCalendarOwnerAttendeeId, status);
return true;
case UPDATE_ALL:
- updateResponse(cr, mEventId, attendeeId, status);
+ updateResponse(cr, mEventId, mCalendarOwnerAttendeeId, status);
return true;
default:
- Log.e("Calendar", "Unexpected choice for updating invitation response");
+ Log.e(TAG, "Unexpected choice for updating invitation response");
break;
}
return false;
}
-
+
private void updateResponse(ContentResolver cr, long eventId, long attendeeId, int status) {
- // Update the "selfAttendeeStatus" field for the event
+ // Update the attendee status in the attendees table. the provider
+ // takes care of updating the self attendance status.
ContentValues values = new ContentValues();
- // Will need to add email when MULTIPLE_ATTENDEES_PER_EVENT supported.
+ if (!TextUtils.isEmpty(mCalendarOwnerAccount)) {
+ values.put(Attendees.ATTENDEE_EMAIL, mCalendarOwnerAccount);
+ }
values.put(Attendees.ATTENDEE_STATUS, status);
values.put(Attendees.EVENT_ID, eventId);
Uri uri = ContentUris.withAppendedId(Attendees.CONTENT_URI, attendeeId);
cr.update(uri, values, null /* where */, null /* selection args */);
}
-
+
private void createExceptionResponse(ContentResolver cr, long eventId,
long attendeeId, int status) {
// Fetch information about the repeating event.
@@ -532,13 +713,13 @@
try {
cursor.moveToFirst();
ContentValues values = new ContentValues();
-
+
String title = cursor.getString(EVENT_INDEX_TITLE);
String timezone = cursor.getString(EVENT_INDEX_EVENT_TIMEZONE);
int calendarId = cursor.getInt(EVENT_INDEX_CALENDAR_ID);
boolean allDay = cursor.getInt(EVENT_INDEX_ALL_DAY) != 0;
String syncId = cursor.getString(EVENT_INDEX_SYNC_ID);
-
+
values.put(Events.TITLE, title);
values.put(Events.EVENT_TIMEZONE, timezone);
values.put(Events.ALL_DAY, allDay ? 1 : 0);
@@ -550,9 +731,9 @@
values.put(Events.ORIGINAL_ALL_DAY, allDay ? 1 : 0);
values.put(Events.STATUS, Events.STATUS_CONFIRMED);
values.put(Events.SELF_ATTENDEE_STATUS, status);
-
+
// Create a recurrence exception
- Uri newUri = cr.insert(Events.CONTENT_URI, values);
+ cr.insert(Events.CONTENT_URI, values);
} finally {
cursor.close();
}
@@ -586,11 +767,10 @@
if (mEventCursor == null) {
return;
}
- Resources res = getResources();
- ContentResolver cr = getContentResolver();
String eventName = mEventCursor.getString(EVENT_INDEX_TITLE);
if (eventName == null || eventName.length() == 0) {
+ Resources res = getResources();
eventName = res.getString(R.string.no_title_label);
}
@@ -600,17 +780,17 @@
String rRule = mEventCursor.getString(EVENT_INDEX_RRULE);
boolean hasAlarm = mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) != 0;
String eventTimezone = mEventCursor.getString(EVENT_INDEX_EVENT_TIMEZONE);
- int color = mEventCursor.getInt(EVENT_INDEX_COLOR) & 0xbbffffff;
+ mColor = mEventCursor.getInt(EVENT_INDEX_COLOR) & 0xbbffffff;
View calBackground = findViewById(R.id.cal_background);
- calBackground.setBackgroundColor(color);
+ calBackground.setBackgroundColor(mColor);
TextView title = (TextView) findViewById(R.id.title);
- title.setTextColor(color);
-
- View divider = (View) findViewById(R.id.divider);
- divider.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_IN);
-
+ title.setTextColor(mColor);
+
+ View divider = findViewById(R.id.divider);
+ divider.getBackground().setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
+
// What
if (eventName != null) {
setTextCommon(R.id.title, eventName);
@@ -670,11 +850,21 @@
if (location == null || location.length() == 0) {
setVisibilityCommon(R.id.where, View.GONE);
} else {
- TextView textView = (TextView) findViewById(R.id.where);
+ final TextView textView = (TextView) findViewById(R.id.where);
if (textView != null) {
textView.setAutoLinkMask(0);
textView.setText(location);
Linkify.addLinks(textView, mWildcardPattern, "geo:0,0?q=");
+ textView.setOnTouchListener(new OnTouchListener() {
+ public boolean onTouch(View v, MotionEvent event) {
+ try {
+ return v.onTouchEvent(event);
+ } catch (ActivityNotFoundException e) {
+ // ignore
+ return true;
+ }
+ }
+ });
}
}
@@ -687,20 +877,149 @@
// Calendar
if (mCalendarsCursor != null) {
- mCalendarsCursor.moveToFirst();
String calendarName = mCalendarsCursor.getString(CALENDARS_INDEX_DISPLAY_NAME);
setTextCommon(R.id.calendar, calendarName);
} else {
setVisibilityCommon(R.id.calendar_container, View.GONE);
}
+ }
- // Response
- updateResponse();
+ private void updateAttendees() {
+ CharSequence[] entries;
+ entries = getResources().getTextArray(R.array.response_labels2);
+ LinearLayout attendeesLayout = (LinearLayout) findViewById(R.id.attendee_list);
+ attendeesLayout.removeAllViewsInLayout();
+ ++mUpdateCounts;
+ addAttendeesToLayout(mAcceptedAttendees, attendeesLayout, entries[0]);
+ addAttendeesToLayout(mDeclinedAttendees, attendeesLayout, entries[2]);
+ addAttendeesToLayout(mTentativeAttendees, attendeesLayout, entries[1]);
+ }
+
+ private void addAttendeesToLayout(ArrayList<Attendee> attendees, LinearLayout attendeeList,
+ CharSequence sectionTitle) {
+ if (attendees.size() == 0) {
+ return;
+ }
+
+ ContentResolver cr = getContentResolver();
+ // Yes/No/Maybe Title
+ View titleView = mLayoutInflater.inflate(R.layout.contact_item, null);
+ titleView.findViewById(R.id.badge).setVisibility(View.GONE);
+ View divider = titleView.findViewById(R.id.separator);
+ divider.getBackground().setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
+
+ TextView title = (TextView) titleView.findViewById(R.id.name);
+ title.setText(getString(R.string.response_label, sectionTitle, attendees.size()));
+ title.setTextAppearance(this, R.style.TextAppearance_EventInfo_Label);
+ attendeeList.addView(titleView);
+
+ // Attendees
+ int numOfAttendees = attendees.size();
+ StringBuilder selection = new StringBuilder(Email.DATA + " IN (");
+ String[] selectionArgs = new String[numOfAttendees];
+
+ for (int i = 0; i < numOfAttendees; ++i) {
+ Attendee attendee = attendees.get(i);
+ selectionArgs[i] = attendee.mEmail;
+
+ View v = mLayoutInflater.inflate(R.layout.contact_item, null);
+ v.setTag(attendee);
+
+ View separator = v.findViewById(R.id.separator);
+ separator.getBackground().setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
+
+ // Text
+ TextView tv = (TextView) v.findViewById(R.id.name);
+ String name = attendee.mName;
+ if (name == null || name.length() == 0) {
+ name = attendee.mEmail;
+ }
+ tv.setText(name);
+
+ ViewHolder vh = new ViewHolder();
+ vh.badge = (QuickContactBadge) v.findViewById(R.id.badge);
+ vh.badge.assignContactFromEmail(attendee.mEmail, true);
+ vh.presence = (ImageView) v.findViewById(R.id.presence);
+ mViewHolders.put(attendee.mEmail, vh);
+
+ if (i == 0) {
+ selection.append('?');
+ } else {
+ selection.append(", ?");
+ }
+
+ attendeeList.addView(v);
+ }
+ selection.append(')');
+
+ mPresenceQueryHandler.startQuery(mUpdateCounts, attendees, CONTACT_DATA_WITH_PRESENCE_URI,
+ PRESENCE_PROJECTION, selection.toString(), selectionArgs, null);
+ }
+
+ private class PresenceQueryHandler extends AsyncQueryHandler {
+ Context mContext;
+ ContentResolver mContentResolver;
+
+ public PresenceQueryHandler(Context context, ContentResolver cr) {
+ super(cr);
+ mContentResolver = cr;
+ mContext = context;
+ }
+
+ @Override
+ protected void onQueryComplete(int queryIndex, Object cookie, Cursor cursor) {
+ if (cursor == null) {
+ if (DEBUG) {
+ Log.e(TAG, "onQueryComplete: cursor == null");
+ }
+ return;
+ }
+
+ try {
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ String email = cursor.getString(PRESENCE_PROJECTION_EMAIL_INDEX);
+ int contactId = cursor.getInt(PRESENCE_PROJECTION_CONTACT_ID_INDEX);
+ ViewHolder vh = mViewHolders.get(email);
+ int photoId = cursor.getInt(PRESENCE_PROJECTION_PHOTO_ID_INDEX);
+ if (DEBUG) {
+ Log.e(TAG, "onQueryComplete Id: " + contactId + " PhotoId: " + photoId
+ + " Email: " + email);
+ }
+ if (vh == null) {
+ continue;
+ }
+ ImageView presenceView = vh.presence;
+ if (presenceView != null) {
+ int status = cursor.getInt(PRESENCE_PROJECTION_PRESENCE_INDEX);
+ presenceView.setImageResource(Presence.getPresenceIconResourceId(status));
+ presenceView.setVisibility(View.VISIBLE);
+ }
+
+ if (photoId > 0 && vh.updateCounts < queryIndex) {
+ vh.updateCounts = queryIndex;
+ Uri personUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ ContactsAsyncHelper.updateImageViewWithContactPhotoAsync(mContext, vh.badge,
+ personUri, R.drawable.ic_contact_picture);
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+ }
}
void updateResponse() {
- if (mVisibility < Calendars.CONTRIBUTOR_ACCESS ||
- mRelationship != Attendees.RELATIONSHIP_ATTENDEE) {
+ // we only let the user accept/reject/etc. a meeting if:
+ // a) you can edit the event's containing calendar AND
+ // b) you're not the organizer and only attendee
+ // (if the attendee data has been hidden, the visible number of attendees
+ // will be 1 -- the calendar owner's).
+ // (there are more cases involved to be 100% accurate, such as
+ // paying attention to whether or not an attendee status was
+ // included in the feed, but we're currently omitting those corner cases
+ // for simplicity).
+ if (!mCanModifyCalendar || (mHasAttendeeData && mIsOrganizer && mNumOfAttendees <= 1)) {
setVisibilityCommon(R.id.response_container, View.GONE);
return;
}
@@ -709,10 +1028,6 @@
Spinner spinner = (Spinner) findViewById(R.id.response_value);
- mOriginalAttendeeResponse = ATTENDEE_NO_RESPONSE;
- if (mAttendeesCursor != null) {
- mOriginalAttendeeResponse = mAttendeesCursor.getInt(ATTENDEES_INDEX_STATUS);
- }
mResponseOffset = 0;
/* If the user has previously responded to this event
@@ -733,7 +1048,12 @@
spinner.setAdapter(adapter);
}
- int index = findResponseIndexFor(mOriginalAttendeeResponse);
+ int index;
+ if (mAttendeeResponseFromIntent != ATTENDEE_NO_RESPONSE) {
+ index = findResponseIndexFor(mAttendeeResponseFromIntent);
+ } else {
+ index = findResponseIndexFor(mOriginalAttendeeResponse);
+ }
spinner.setSelection(index + mResponseOffset);
spinner.setOnItemSelectedListener(this);
}
@@ -752,4 +1072,39 @@
}
return;
}
+
+ /**
+ * Taken from com.google.android.gm.HtmlConversationActivity
+ *
+ * Send the intent that shows the Contact info corresponding to the email address.
+ */
+ public void showContactInfo(Attendee attendee, Rect rect) {
+ // First perform lookup query to find existing contact
+ final ContentResolver resolver = getContentResolver();
+ final String address = attendee.mEmail;
+ final Uri dataUri = Uri.withAppendedPath(CommonDataKinds.Email.CONTENT_FILTER_URI,
+ Uri.encode(address));
+ final Uri lookupUri = ContactsContract.Data.getContactLookupUri(resolver, dataUri);
+
+ if (lookupUri != null) {
+ // Found matching contact, trigger QuickContact
+ QuickContact.showQuickContact(this, rect, lookupUri, QuickContact.MODE_MEDIUM, null);
+ } else {
+ // No matching contact, ask user to create one
+ final Uri mailUri = Uri.fromParts("mailto", address, null);
+ final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, mailUri);
+
+ // Pass along full E-mail string for possible create dialog
+ Rfc822Token sender = new Rfc822Token(attendee.mName, attendee.mEmail, null);
+ intent.putExtra(Intents.EXTRA_CREATE_DESCRIPTION, sender.toString());
+
+ // Only provide personal name hint if we have one
+ final String senderPersonal = attendee.mName;
+ if (!TextUtils.isEmpty(senderPersonal)) {
+ intent.putExtra(Intents.Insert.NAME, senderPersonal);
+ }
+
+ startActivity(intent);
+ }
+ }
}
diff --git a/src/com/android/calendar/GoogleCalendarUriIntentFilter.java b/src/com/android/calendar/GoogleCalendarUriIntentFilter.java
new file mode 100644
index 0000000..502243a
--- /dev/null
+++ b/src/com/android/calendar/GoogleCalendarUriIntentFilter.java
@@ -0,0 +1,118 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** 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 the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** See the License for the specific language governing permissions and
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** limitations under the License.
+*/
+
+package com.android.calendar;
+
+import static android.provider.Calendar.EVENT_BEGIN_TIME;
+import static android.provider.Calendar.EVENT_END_TIME;
+import static android.provider.Calendar.AttendeesColumns.ATTENDEE_STATUS;
+import static android.provider.Calendar.AttendeesColumns.ATTENDEE_STATUS_ACCEPTED;
+import static android.provider.Calendar.AttendeesColumns.ATTENDEE_STATUS_DECLINED;
+import static android.provider.Calendar.AttendeesColumns.ATTENDEE_STATUS_NONE;
+import static android.provider.Calendar.AttendeesColumns.ATTENDEE_STATUS_TENTATIVE;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Calendar.Events;
+
+public class GoogleCalendarUriIntentFilter extends Activity {
+ private static final int EVENT_INDEX_ID = 0;
+ private static final int EVENT_INDEX_START = 1;
+ private static final int EVENT_INDEX_END = 2;
+
+ private static final String[] EVENT_PROJECTION = new String[] {
+ Events._ID, // 0
+ Events.DTSTART, // 1
+ Events.DTEND, // 2
+ };
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ Intent intent = getIntent();
+ if (intent != null) {
+ Uri uri = intent.getData();
+ if (uri != null) {
+ String eid = uri.getQueryParameter("eid");
+ if (eid != null) {
+ ContentResolver cr = getContentResolver();
+ String selection = Events.HTML_URI + " LIKE \"%eid=" + eid + "%\"";
+
+ Cursor eventCursor = managedQuery(Events.CONTENT_URI, EVENT_PROJECTION,
+ selection, null);
+
+ // TODO what to do when there's more than one match
+ if (eventCursor != null && eventCursor.getCount() > 0) {
+ // Get info from Cursor
+ eventCursor.moveToFirst();
+ int eventId = eventCursor.getInt(EVENT_INDEX_ID);
+ long startMillis = eventCursor.getLong(EVENT_INDEX_START);
+ long endMillis = eventCursor.getLong(EVENT_INDEX_END);
+
+ // Pick up attendee status action from uri clicked
+ int attendeeStatus = ATTENDEE_STATUS_NONE;
+ if ("RESPOND".equals(uri.getQueryParameter("action"))) {
+ try {
+ switch (Integer.parseInt(uri.getQueryParameter("rst"))) {
+ case 1: // Yes
+ attendeeStatus = ATTENDEE_STATUS_ACCEPTED;
+ break;
+ case 2: // No
+ attendeeStatus = ATTENDEE_STATUS_DECLINED;
+ break;
+ case 3: // Maybe
+ attendeeStatus = ATTENDEE_STATUS_TENTATIVE;
+ break;
+ }
+ } catch (NumberFormatException e) {
+ // ignore this error as if the response code
+ // wasn't in the uri.
+ }
+ }
+
+ // Send intent to calendar app
+ Uri calendarUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
+ intent = new Intent(Intent.ACTION_VIEW, calendarUri);
+ intent.putExtra(EVENT_BEGIN_TIME, startMillis);
+ intent.putExtra(EVENT_END_TIME, endMillis);
+ if (attendeeStatus != ATTENDEE_STATUS_NONE) {
+ intent.putExtra(ATTENDEE_STATUS, attendeeStatus);
+ }
+ startActivity(intent);
+ finish();
+ return;
+ }
+ }
+ }
+
+ // Can't handle the intent. Pass it on to the next Activity.
+ try {
+ startNextMatchingActivity(intent);
+ } catch (ActivityNotFoundException ex) {
+ // no browser installed? Just drop it.
+ }
+ }
+ finish();
+ }
+}
diff --git a/src/com/android/calendar/IcsImportActivity.java b/src/com/android/calendar/IcsImportActivity.java
index c4f5fde..c5a125d 100644
--- a/src/com/android/calendar/IcsImportActivity.java
+++ b/src/com/android/calendar/IcsImportActivity.java
@@ -60,11 +60,9 @@
}
}
- private View mView;
private Button mImportButton;
private Button mCancelButton;
private Spinner mCalendars;
- private ImageView mCalendarIcon;
private TextView mNumEvents;
private ICalendar.Component mCalendar = null;
@@ -86,9 +84,7 @@
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.ics_import_activity);
- mView = findViewById(R.id.import_ics);
- mCalendarIcon = (ImageView) findViewById(R.id.calendar_icon);
mCalendars = (Spinner) findViewById(R.id.calendars);
populateCalendars();
diff --git a/src/com/android/calendar/LaunchActivity.java b/src/com/android/calendar/LaunchActivity.java
index 3d61585..36d6ab6 100644
--- a/src/com/android/calendar/LaunchActivity.java
+++ b/src/com/android/calendar/LaunchActivity.java
@@ -16,27 +16,26 @@
package com.android.calendar;
-import com.google.android.googlelogin.GoogleLoginServiceConstants;
-import com.google.android.googlelogin.GoogleLoginServiceHelper;
-
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
import android.app.Activity;
-import android.content.ContentValues;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
-import android.provider.Calendar.Calendars;
import android.provider.Gmail;
-import java.util.TimeZone;
+import com.google.android.googlelogin.GoogleLoginServiceConstants;
+
+import java.io.IOException;
public class LaunchActivity extends Activity {
static final String KEY_DETAIL_VIEW = "DETAIL_VIEW";
-
- // An arbitrary constant to pass to the GoogleLoginHelperService
- private static final int GET_ACCOUNT_REQUEST = 1;
private Bundle mExtras;
@Override
@@ -47,26 +46,39 @@
// Our UI is not something intended for the user to see. We just
// stick around until we can figure out what to do next based on
// the current state of the system.
- setVisible(false);
+ // TODO: Removed until framework is fixed in b/2008662
+ // setVisible(false);
// Only try looking for an account if this is the first launch.
if (icicle == null) {
// This will request a Gmail account and if none are present, it will
// invoke SetupWizard to login or create one. The result is returned
- // through onActivityResult().
+ // via the Future2Callback.
Bundle bundle = new Bundle();
bundle.putCharSequence("optional_message", getText(R.string.calendar_plug));
- GoogleLoginServiceHelper.getCredentials(
- this,
- GET_ACCOUNT_REQUEST,
- bundle,
- GoogleLoginServiceConstants.PREFER_HOSTED,
- Gmail.GMAIL_AUTH_SERVICE,
- true);
+ AccountManager.get(this).getAuthTokenByFeatures(
+ GoogleLoginServiceConstants.ACCOUNT_TYPE, Gmail.GMAIL_AUTH_SERVICE,
+ new String[]{GoogleLoginServiceConstants.FEATURE_LEGACY_HOSTED_OR_GOOGLE}, this,
+ bundle, null /* loginOptions */, new AccountManagerCallback<Bundle>() {
+ public void run(AccountManagerFuture<Bundle> future) {
+ try {
+ Bundle result = future.getResult();
+ onAccountsLoaded(new Account(
+ result.getString(GoogleLoginServiceConstants.AUTH_ACCOUNT_KEY),
+ result.getString(AccountManager.KEY_ACCOUNT_TYPE)));
+ } catch (OperationCanceledException e) {
+ finish();
+ } catch (IOException e) {
+ finish();
+ } catch (AuthenticatorException e) {
+ finish();
+ }
+ }
+ }, null /* handler */);
}
}
- private void onAccountsLoaded(String account) {
+ private void onAccountsLoaded(Account account) {
// Get the data for from this intent, if any
Intent myIntent = getIntent();
Uri myData = myIntent.getData();
@@ -91,57 +103,8 @@
CalendarPreferenceActivity.DEFAULT_START_VIEW);
intent.setClassName(this, startActivity);
+ intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
finish();
}
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
- super.onActivityResult(requestCode, resultCode, intent);
- if (requestCode == GET_ACCOUNT_REQUEST) {
- String account = null;
- if (resultCode == RESULT_OK) {
- // if we got a response from the sub-activity, it's supposed to hold account data
- if (intent != null) {
- Bundle extras = intent.getExtras();
- if (extras != null) {
- account = extras.getString(GoogleLoginServiceConstants.AUTH_ACCOUNT_KEY);
- }
- }
- } else {
- // otherwise, create a local calendar if there isn't one already
- Cursor cur = getContentResolver().query(Calendars.CONTENT_URI,
- null, null, null, null);
- if (cur != null) {
- if (cur.getCount() != 0) {
- cur.moveToFirst();
- try {
- account = cur.getString(cur.getColumnIndexOrThrow(Calendars.NAME));
- } catch(RuntimeException e) {
- // ignore - this leaves account == null, which is fine
- }
- } else {
- account = "nobody@localhost";
- // inspired from CalendarProvider.onAccountsChanged
- ContentValues vals = new ContentValues();
- vals.put(Calendars.ACCESS_LEVEL, Integer.toString(Calendars.OWNER_ACCESS));
- vals.put(Calendars.COLOR, -14069085);
- vals.put(Calendars.DISPLAY_NAME, "Default");
- vals.put(Calendars.HIDDEN, 0);
- vals.put(Calendars.NAME, account);
- vals.put(Calendars.SELECTED, 1);
- vals.put(Calendars.SYNC_EVENTS, 1);
- vals.put(Calendars.TIMEZONE, TimeZone.getDefault().getID());
- getContentResolver().insert(Calendars.CONTENT_URI, vals);
- }
- cur.close();
- }
- }
- if (account != null) {
- onAccountsLoaded(account);
- } else {
- finish();
- }
- }
- }
}
diff --git a/src/com/android/calendar/MenuHelper.java b/src/com/android/calendar/MenuHelper.java
index f23cbf9..6ed1512 100644
--- a/src/com/android/calendar/MenuHelper.java
+++ b/src/com/android/calendar/MenuHelper.java
@@ -139,23 +139,19 @@
nav.goToToday();
return true;
case MENU_PREFERENCES:
- switchTo(activity, CalendarPreferenceActivity.class.getName(), nav.getSelectedTime());
+ Utils.startActivity(activity, CalendarPreferenceActivity.class.getName(), nav.getSelectedTime());
return true;
case MENU_AGENDA:
- switchTo(activity, AgendaActivity.class.getName(), nav.getSelectedTime());
- activity.finish();
+ Utils.startActivity(activity, AgendaActivity.class.getName(), nav.getSelectedTime());
return true;
case MENU_DAY:
- switchTo(activity, DayActivity.class.getName(), nav.getSelectedTime());
- activity.finish();
+ Utils.startActivity(activity, DayActivity.class.getName(), nav.getSelectedTime());
return true;
case MENU_WEEK:
- switchTo(activity, WeekActivity.class.getName(), nav.getSelectedTime());
- activity.finish();
+ Utils.startActivity(activity, WeekActivity.class.getName(), nav.getSelectedTime());
return true;
case MENU_MONTH:
- switchTo(activity, MonthActivity.class.getName(), nav.getSelectedTime());
- activity.finish();
+ Utils.startActivity(activity, MonthActivity.class.getName(), nav.getSelectedTime());
return true;
case MENU_EVENT_CREATE: {
long startMillis = nav.getSelectedTime();
@@ -171,11 +167,4 @@
}
return false;
}
-
- /* package */ static void switchTo(Activity activity, String className, long startMillis) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setClassName(activity, className);
- intent.putExtra(EVENT_BEGIN_TIME, startMillis);
- activity.startActivity(intent);
- }
}
diff --git a/src/com/android/calendar/MonthActivity.java b/src/com/android/calendar/MonthActivity.java
index 89b07ec..62f8602 100644
--- a/src/com/android/calendar/MonthActivity.java
+++ b/src/com/android/calendar/MonthActivity.java
@@ -17,7 +17,6 @@
package com.android.calendar;
import static android.provider.Calendar.EVENT_BEGIN_TIME;
-import dalvik.system.VMRuntime;
import android.app.Activity;
import android.content.BroadcastReceiver;
@@ -45,6 +44,8 @@
import android.widget.ViewSwitcher;
import android.widget.Gallery.LayoutParams;
+import dalvik.system.VMRuntime;
+
import java.util.Calendar;
public class MonthActivity extends Activity implements ViewSwitcher.ViewFactory,
@@ -83,7 +84,7 @@
}
/* Navigator interface methods */
- public void goTo(Time time) {
+ public void goTo(Time time, boolean animate) {
TextView title = (TextView) findViewById(R.id.title);
title.setText(Utils.formatMonthYear(time));
@@ -96,14 +97,16 @@
// two adjacent months.
// This is faster than calling getSelectedTime() because we avoid
// a call to Time#normalize().
- int currentMonth = currentTime.month + currentTime.year * 12;
- int nextMonth = time.month + time.year * 12;
- if (nextMonth < currentMonth) {
- mSwitcher.setInAnimation(mInAnimationPast);
- mSwitcher.setOutAnimation(mOutAnimationPast);
- } else {
- mSwitcher.setInAnimation(mInAnimationFuture);
- mSwitcher.setOutAnimation(mOutAnimationFuture);
+ if (animate) {
+ int currentMonth = currentTime.month + currentTime.year * 12;
+ int nextMonth = time.month + time.year * 12;
+ if (nextMonth < currentMonth) {
+ mSwitcher.setInAnimation(mInAnimationPast);
+ mSwitcher.setOutAnimation(mOutAnimationPast);
+ } else {
+ mSwitcher.setInAnimation(mInAnimationFuture);
+ mSwitcher.setOutAnimation(mOutAnimationFuture);
+ }
}
MonthView next = (MonthView) mSwitcher.getNextView();
@@ -119,6 +122,9 @@
public void goToToday() {
Time now = new Time();
now.set(System.currentTimeMillis());
+ now.minute = 0;
+ now.second = 0;
+ now.normalize(false);
TextView title = (TextView) findViewById(R.id.title);
title.setText(Utils.formatMonthYear(now));
@@ -195,7 +201,7 @@
// Eliminate extra GCs during startup by setting the initial heap size to 4MB.
// TODO: We should restore the old heap size once the activity reaches the idle state
- long oldHeapSize = VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);
+ VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);
setContentView(R.layout.month_activity);
mContentResolver = getContentResolver();
@@ -258,6 +264,16 @@
}
@Override
+ protected void onNewIntent(Intent intent) {
+ long timeMillis = Utils.timeFromIntentInMillis(intent);
+ if (timeMillis > 0) {
+ Time time = new Time();
+ time.set(timeMillis);
+ goTo(time, false);
+ }
+ }
+
+ @Override
protected void onPause() {
super.onPause();
if (isFinishing()) {
@@ -271,6 +287,9 @@
view = (MonthView) mSwitcher.getNextView();
view.dismissPopup();
mEventLoader.stopBackgroundThread();
+
+ // Record Month View as the (new) start view
+ Utils.setDefaultView(this, CalendarApplication.MONTH_VIEW_ID);
}
@Override
@@ -287,12 +306,6 @@
view1.setDetailedView(str);
view2.setDetailedView(str);
- // Record Month View as the (new) start view
- String activityString = CalendarApplication.ACTIVITY_NAMES[CalendarApplication.MONTH_VIEW_ID];
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(CalendarPreferenceActivity.KEY_START_VIEW, activityString);
- editor.commit();
-
// Register for Intent broadcasts
IntentFilter filter = new IntentFilter();
@@ -312,16 +325,6 @@
}
@Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_BACK:
- finish();
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuHelper.onPrepareOptionsMenu(this, menu);
return super.onPrepareOptionsMenu(menu);
diff --git a/src/com/android/calendar/MonthView.java b/src/com/android/calendar/MonthView.java
index f6db3b8..13f6abc 100644
--- a/src/com/android/calendar/MonthView.java
+++ b/src/com/android/calendar/MonthView.java
@@ -60,19 +60,20 @@
private static final boolean PROFILE_LOAD_TIME = false;
private static final boolean DEBUG_BUSYBITS = false;
- private static final int WEEK_GAP = 0;
- private static final int MONTH_DAY_GAP = 1;
- private static final float HOUR_GAP = 0.5f;
+ private static float mScale = 0; // Used for supporting different screen densities
+ private static int WEEK_GAP = 0;
+ private static int MONTH_DAY_GAP = 1;
+ private static float HOUR_GAP = 0.5f;
- private static final int MONTH_DAY_TEXT_SIZE = 20;
- private static final int WEEK_BANNER_HEIGHT = 17;
- private static final int WEEK_TEXT_SIZE = 15;
- private static final int WEEK_TEXT_PADDING = 3;
- private static final int BUSYBIT_WIDTH = 10;
- private static final int BUSYBIT_RIGHT_MARGIN = 3;
- private static final int BUSYBIT_TOP_BOTTOM_MARGIN = 7;
+ private static int MONTH_DAY_TEXT_SIZE = 20;
+ private static int WEEK_BANNER_HEIGHT = 17;
+ private static int WEEK_TEXT_SIZE = 15;
+ private static int WEEK_TEXT_PADDING = 3;
+ private static int BUSYBIT_WIDTH = 10;
+ private static int BUSYBIT_RIGHT_MARGIN = 3;
+ private static int BUSYBIT_TOP_BOTTOM_MARGIN = 7;
- private static final int HORIZONTAL_FLING_THRESHOLD = 50;
+ private static int HORIZONTAL_FLING_THRESHOLD = 50;
private int mCellHeight;
private int mBorder;
@@ -196,6 +197,25 @@
public MonthView(MonthActivity activity, Navigator navigator) {
super(activity);
+ if (mScale == 0) {
+ mScale = getContext().getResources().getDisplayMetrics().density;
+ if (mScale != 1) {
+ WEEK_GAP *= mScale;
+ MONTH_DAY_GAP *= mScale;
+ HOUR_GAP *= mScale;
+
+ MONTH_DAY_TEXT_SIZE *= mScale;
+ WEEK_BANNER_HEIGHT *= mScale;
+ WEEK_TEXT_SIZE *= mScale;
+ WEEK_TEXT_PADDING *= mScale;
+ BUSYBIT_WIDTH *= mScale;
+ BUSYBIT_RIGHT_MARGIN *= mScale;
+ BUSYBIT_TOP_BOTTOM_MARGIN *= mScale;
+
+ HORIZONTAL_FLING_THRESHOLD *= mScale;
+ }
+ }
+
mEventLoader = activity.mEventLoader;
mNavigator = navigator;
mEventGeometry = new EventGeometry();
@@ -282,7 +302,7 @@
time.month -= 1;
}
time.normalize(true);
- mParentActivity.goTo(time);
+ mParentActivity.goTo(time, true);
return true;
}
@@ -358,7 +378,6 @@
int y = (int) e.getY();
long millis = getSelectedMillisFor(x, y);
Utils.startActivity(getContext(), mDetailedView, millis);
- mParentActivity.finish();
}
return true;
@@ -397,14 +416,12 @@
switch (item.getItemId()) {
case MenuHelper.MENU_DAY: {
long startMillis = getSelectedTimeInMillis();
- MenuHelper.switchTo(mParentActivity, DayActivity.class.getName(), startMillis);
- mParentActivity.finish();
+ Utils.startActivity(mParentActivity, DayActivity.class.getName(), startMillis);
break;
}
case MenuHelper.MENU_AGENDA: {
long startMillis = getSelectedTimeInMillis();
- MenuHelper.switchTo(mParentActivity, AgendaActivity.class.getName(), startMillis);
- mParentActivity.finish();
+ Utils.startActivity(mParentActivity, AgendaActivity.class.getName(), startMillis);
break;
}
case MenuHelper.MENU_EVENT_CREATE: {
@@ -1233,7 +1250,6 @@
if (duration < ViewConfiguration.getLongPressTimeout()) {
long millis = getSelectedTimeInMillis();
Utils.startActivity(getContext(), mDetailedView, millis);
- mParentActivity.finish();
} else {
mSelectionMode = SELECTION_LONGPRESS;
mRedrawScreen = true;
@@ -1274,7 +1290,6 @@
case KeyEvent.KEYCODE_ENTER:
long millis = getSelectedTimeInMillis();
Utils.startActivity(getContext(), mDetailedView, millis);
- mParentActivity.finish();
return true;
case KeyEvent.KEYCODE_DPAD_UP:
if (mCursor.up()) {
@@ -1331,7 +1346,7 @@
if (other != null) {
other.normalize(true /* ignore DST */);
- mNavigator.goTo(other);
+ mNavigator.goTo(other, true);
} else if (redraw) {
mRedrawScreen = true;
invalidate();
diff --git a/src/com/android/calendar/Navigator.java b/src/com/android/calendar/Navigator.java
index e05a603..c8de34b 100644
--- a/src/com/android/calendar/Navigator.java
+++ b/src/com/android/calendar/Navigator.java
@@ -28,8 +28,9 @@
/**
* Changes the view to include the given time.
* @param time the desired time to view.
+ * @animate enable animation
*/
- void goTo(Time time);
+ void goTo(Time time, boolean animate);
/**
* Changes the view to include today's date.
diff --git a/src/com/android/calendar/SelectCalendarsActivity.java b/src/com/android/calendar/SelectCalendarsActivity.java
index 01037ab..429ec41 100644
--- a/src/com/android/calendar/SelectCalendarsActivity.java
+++ b/src/com/android/calendar/SelectCalendarsActivity.java
@@ -18,16 +18,18 @@
import android.app.Activity;
import android.app.AlertDialog;
-import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
+import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.provider.Calendar.Calendars;
+import android.provider.Calendar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -45,8 +47,8 @@
private static final String TAG = "Calendar";
private View mView = null;
private Cursor mCursor = null;
- private QueryHandler mQueryHandler;
private SelectCalendarsAdapter mAdapter;
+ private ContentResolver mContentResolver;
private static final String[] PROJECTION = new String[] {
Calendars._ID,
Calendars.DISPLAY_NAME,
@@ -62,7 +64,6 @@
setContentView(R.layout.calendars_activity);
getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
Window.PROGRESS_INDETERMINATE_ON);
- mQueryHandler = new QueryHandler(getContentResolver());
mView = findViewById(R.id.calendars);
ListView items = (ListView) mView.findViewById(R.id.items);
Context context = mView.getContext();
@@ -70,18 +71,42 @@
Calendars.SYNC_EVENTS + "=1",
null /* selectionArgs */,
Calendars.DEFAULT_SORT_ORDER);
-
+ mContentResolver = getContentResolver();
mAdapter = new SelectCalendarsAdapter(context, mCursor);
items.setAdapter(mAdapter);
items.setOnItemClickListener(this);
// Start a background sync to get the list of calendars from the server.
- startCalendarSync();
+ startCalendarMetafeedSync();
}
+ // Create an observer so that we can update the views whenever a
+ // Calendar changes.
+ private ContentObserver mObserver = new ContentObserver(new Handler())
+ {
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ if (!isFinishing()) {
+ mCursor.requery();
+ }
+ }
+ };
+
@Override
public void onPause() {
super.onPause();
+ mContentResolver.unregisterContentObserver(mObserver);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mContentResolver.registerContentObserver(Calendar.Events.CONTENT_URI, true, mObserver);
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -115,13 +140,11 @@
int mNumItems;
long[] mCalendarIds;
boolean[] mIsChecked;
- ContentResolver mContentResolver;
boolean mRemove;
private int mCheckedCount;
private Button mOkButtonInAddDeleteCalendar;
public ChangeCalendarAction(boolean remove) {
- mContentResolver = SelectCalendarsActivity.this.getContentResolver();
mRemove = remove;
}
@@ -169,14 +192,14 @@
values.put(Calendars.SYNC_EVENTS, selected);
mContentResolver.update(uri, values, null, null);
}
-
+
// If there were any changes, then update the list of calendars
// that are synced.
if (changesFound) {
mCursor.requery();
}
}
-
+
public boolean onMenuItemClick(MenuItem item) {
AlertDialog.Builder builder = new AlertDialog.Builder(SelectCalendarsActivity.this);
String selection;
@@ -188,9 +211,7 @@
builder.setTitle(R.string.add_calendars);
selection = Calendars.SYNC_EVENTS + "=0";
}
- ContentResolver cr = getContentResolver();
- // TODO this can cause ANRs http://b/1736511
- Cursor cursor = cr.query(Calendars.CONTENT_URI, PROJECTION,
+ Cursor cursor = mContentResolver.query(Calendars.CONTENT_URI, PROJECTION,
selection, null /* selectionArgs */,
Calendars.DEFAULT_SORT_ORDER);
if (cursor == null) {
@@ -226,69 +247,22 @@
}
}
- private class QueryHandler extends AsyncQueryHandler {
- public QueryHandler(ContentResolver cr) {
- super(cr);
- }
-
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
- Window.PROGRESS_VISIBILITY_OFF);
-
- // If the Activity is finishing, then close the cursor.
- // Otherwise, use the new cursor in the adapter.
- if (isFinishing()) {
- stopManagingCursor(cursor);
- cursor.close();
- } else {
- if (cursor.getCount() == 0) {
- // There are no calendars. This might happen if we lost
- // the wireless connection (in airplane mode, for example).
- // Leave the current list of calendars alone and pop up
- // a dialog explaining that the connection is down.
- // But allow the user to add and remove calendars.
- return;
- }
- if (mCursor != null) {
- stopManagingCursor(mCursor);
- }
- mCursor = cursor;
- startManagingCursor(cursor);
- mAdapter.changeCursor(cursor);
- }
- }
- }
-
- // This class implements the menu option "Refresh list from server".
- // (No longer used.)
- public class RefreshAction implements Runnable {
- public void run() {
- startCalendarSync();
- }
- }
-
- // startCalendarSync() checks the server for an updated list of Calendars
- // (in the background) using an AsyncQueryHandler.
+ // startCalendarMetafeedSync() checks the server for an updated list of
+ // Calendars (in the background).
//
- // Calendars are never removed from the phone due to a server sync.
- // But if a Calendar is added on the web (and it is selected and not
+ // If a Calendar is added on the web (and it is selected and not
// hidden) then it will be added to the list of calendars on the phone
- // (when this asynchronous query finishes). When a new calendar from the
+ // (when this finishes). When a new calendar from the
// web is added to the phone, then the events for that calendar are also
// downloaded from the web.
//
// This sync is done automatically in the background when the
// SelectCalendars activity is started.
- private void startCalendarSync() {
- getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
- Window.PROGRESS_VISIBILITY_ON);
-
- // TODO: make sure the user has login info.
-
- Uri uri = Calendars.LIVE_CONTENT_URI;
- mQueryHandler.startQuery(0, null, uri, PROJECTION,
- Calendars.SYNC_EVENTS + "=1",
- null, Calendars.DEFAULT_SORT_ORDER);
+ private void startCalendarMetafeedSync() {
+ Bundle extras = new Bundle();
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+ extras.putBoolean("metafeedonly", true);
+ ContentResolver.requestSync(null /* all accounts */,
+ Calendars.CONTENT_URI.getAuthority(), extras);
}
}
diff --git a/src/com/android/calendar/Utils.java b/src/com/android/calendar/Utils.java
index 53c584c..c282798 100644
--- a/src/com/android/calendar/Utils.java
+++ b/src/com/android/calendar/Utils.java
@@ -17,9 +17,12 @@
package com.android.calendar;
import static android.provider.Calendar.EVENT_BEGIN_TIME;
+
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.res.Resources;
+import android.preference.PreferenceManager;
import android.text.format.Time;
import android.view.animation.AlphaAnimation;
import android.widget.ViewFlipper;
@@ -30,10 +33,27 @@
intent.setClassName(context, className);
intent.putExtra(EVENT_BEGIN_TIME, time);
+ intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
+ static void setDefaultView(Context context, int viewId) {
+ String activityString = CalendarApplication.ACTIVITY_NAMES[viewId];
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = prefs.edit();
+ if (viewId == CalendarApplication.AGENDA_VIEW_ID ||
+ viewId == CalendarApplication.DAY_VIEW_ID) {
+ // Record the (new) detail start view only for Agenda and Day
+ editor.putString(CalendarPreferenceActivity.KEY_DETAILED_VIEW, activityString);
+ }
+
+ // Record the (new) start view
+ editor.putString(CalendarPreferenceActivity.KEY_START_VIEW, activityString);
+ editor.commit();
+ }
+
public static final Time timeFromIntent(Intent intent) {
Time time = new Time();
time.set(timeFromIntentInMillis(intent));
diff --git a/src/com/android/calendar/WeekActivity.java b/src/com/android/calendar/WeekActivity.java
index 0e06b51..43015f6 100644
--- a/src/com/android/calendar/WeekActivity.java
+++ b/src/com/android/calendar/WeekActivity.java
@@ -16,9 +16,11 @@
package com.android.calendar;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
+import android.text.format.Time;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ProgressBar;
@@ -55,6 +57,16 @@
}
@Override
+ protected void onNewIntent(Intent intent) {
+ long timeMillis = Utils.timeFromIntentInMillis(intent);
+ if (timeMillis > 0) {
+ Time time = new Time();
+ time.set(timeMillis);
+ goTo(time, false);
+ }
+ }
+
+ @Override
protected void onResume() {
super.onResume();
@@ -66,12 +78,6 @@
CalendarPreferenceActivity.DEFAULT_DETAILED_VIEW);
view1.setDetailedView(str);
view2.setDetailedView(str);
-
- // Record Week View as the (new) start view
- String activityString = CalendarApplication.ACTIVITY_NAMES[CalendarApplication.WEEK_VIEW_ID];
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(CalendarPreferenceActivity.KEY_START_VIEW, activityString);
- editor.commit();
}
@Override
@@ -79,5 +85,8 @@
super.onPause();
CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
mSelectedDay = view.getSelectedDay();
+
+ // Record Week View as the (new) start view
+ Utils.setDefaultView(this, CalendarApplication.WEEK_VIEW_ID);
}
}
diff --git a/src/com/android/calendar/WeekView.java b/src/com/android/calendar/WeekView.java
index b0b3244..ebff44c 100644
--- a/src/com/android/calendar/WeekView.java
+++ b/src/com/android/calendar/WeekView.java
@@ -26,7 +26,7 @@
}
private void init() {
- mDrawTextInEventRect = false;
+ mDrawTextInEventRect = true;
mNumDays = 7;
mEventGeometry.setCellMargin(CELL_MARGIN);
}
diff --git a/tests/src/com/android/calendar/FormatDateRangeTest.java b/tests/src/com/android/calendar/FormatDateRangeTest.java
index 6f0d775..548fc1c 100644
--- a/tests/src/com/android/calendar/FormatDateRangeTest.java
+++ b/tests/src/com/android/calendar/FormatDateRangeTest.java
@@ -30,7 +30,7 @@
*/
public class FormatDateRangeTest extends AndroidTestCase {
- private class DateTest {
+ static private class DateTest {
public Time date1;
public Time date2;
public int flags;
diff --git a/tests/src/com/android/calendar/WeekNumberTest.java b/tests/src/com/android/calendar/WeekNumberTest.java
index 6b3774b..01f5746 100644
--- a/tests/src/com/android/calendar/WeekNumberTest.java
+++ b/tests/src/com/android/calendar/WeekNumberTest.java
@@ -29,7 +29,7 @@
*/
public class WeekNumberTest extends AndroidTestCase {
- private class DateAndWeekNumber {
+ static private class DateAndWeekNumber {
public Time date;
public Time allDayDate;
public int expectedWeekNumber;