diff options
34 files changed, 1356 insertions, 247 deletions
diff --git a/res/drawable-hdpi/ic_colorpicker_swatch_selected.png b/res/drawable-hdpi/ic_colorpicker_swatch_selected.png Binary files differnew file mode 100644 index 00000000..3cbfe1a8 --- /dev/null +++ b/res/drawable-hdpi/ic_colorpicker_swatch_selected.png diff --git a/res/drawable-hdpi/ic_menu_colorpicker_holo_dark.png b/res/drawable-hdpi/ic_menu_colorpicker_holo_dark.png Binary files differnew file mode 100644 index 00000000..df143ede --- /dev/null +++ b/res/drawable-hdpi/ic_menu_colorpicker_holo_dark.png diff --git a/res/drawable-hdpi/ic_menu_colorpicker_holo_light.png b/res/drawable-hdpi/ic_menu_colorpicker_holo_light.png Binary files differnew file mode 100644 index 00000000..fc7a1a19 --- /dev/null +++ b/res/drawable-hdpi/ic_menu_colorpicker_holo_light.png diff --git a/res/drawable-mdpi/ic_colorpicker_swatch_selected.png b/res/drawable-mdpi/ic_colorpicker_swatch_selected.png Binary files differnew file mode 100644 index 00000000..acbdecac --- /dev/null +++ b/res/drawable-mdpi/ic_colorpicker_swatch_selected.png diff --git a/res/drawable-mdpi/ic_menu_colorpicker_holo_dark.png b/res/drawable-mdpi/ic_menu_colorpicker_holo_dark.png Binary files differnew file mode 100644 index 00000000..246488a4 --- /dev/null +++ b/res/drawable-mdpi/ic_menu_colorpicker_holo_dark.png diff --git a/res/drawable-mdpi/ic_menu_colorpicker_holo_light.png b/res/drawable-mdpi/ic_menu_colorpicker_holo_light.png Binary files differnew file mode 100644 index 00000000..6ad4fede --- /dev/null +++ b/res/drawable-mdpi/ic_menu_colorpicker_holo_light.png diff --git a/res/drawable-xhdpi/ic_colorpicker_swatch_selected.png b/res/drawable-xhdpi/ic_colorpicker_swatch_selected.png Binary files differnew file mode 100644 index 00000000..812ff2c3 --- /dev/null +++ b/res/drawable-xhdpi/ic_colorpicker_swatch_selected.png diff --git a/res/drawable-xhdpi/ic_menu_colorpicker_holo_dark.png b/res/drawable-xhdpi/ic_menu_colorpicker_holo_dark.png Binary files differnew file mode 100644 index 00000000..03a84a8c --- /dev/null +++ b/res/drawable-xhdpi/ic_menu_colorpicker_holo_dark.png diff --git a/res/drawable-xhdpi/ic_menu_colorpicker_holo_light.png b/res/drawable-xhdpi/ic_menu_colorpicker_holo_light.png Binary files differnew file mode 100644 index 00000000..1a0b2cd9 --- /dev/null +++ b/res/drawable-xhdpi/ic_menu_colorpicker_holo_light.png diff --git a/res/drawable/color_picker_swatch.xml b/res/drawable/color_picker_swatch.xml new file mode 100644 index 00000000..db71091a --- /dev/null +++ b/res/drawable/color_picker_swatch.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" />
\ No newline at end of file diff --git a/res/layout-sw600dp/edit_event_1.xml b/res/layout-sw600dp/edit_event_1.xml index f5935cc5..0c18282d 100644 --- a/res/layout-sw600dp/edit_event_1.xml +++ b/res/layout-sw600dp/edit_event_1.xml @@ -28,24 +28,28 @@ android:text="@string/edit_event_calendar_label" style="@style/TextAppearance.EditEvent_Label" android:gravity="center_vertical" /> - <FrameLayout - android:id="@+id/calendar_selector_wrapper" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:layout_marginRight="12dip" - android:layout_marginLeft="12dip" - android:focusable="true" > - <Spinner - android:id="@+id/calendars_spinner" - android:prompt="@string/edit_event_calendar_label" - android:layout_gravity="center_vertical" - android:gravity="center_vertical" - android:layout_height="wrap_content" + <LinearLayout + android:id="@+id/calendar_selector_wrapper" android:layout_width="match_parent" - android:layout_marginRight="0dip" - android:layout_marginLeft="0dip" - style="@style/TextAppearance.EditEvent_Spinner"/> - </FrameLayout> + android:layout_height="wrap_content" + android:layout_marginLeft="12dip" + android:layout_marginRight="12dip" + android:focusable="true" > + <Spinner + android:id="@+id/calendars_spinner" + style="@style/TextAppearance.EditEvent_Spinner" + android:layout_width="0dip" + android:layout_weight="1" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginLeft="0dip" + android:layout_marginRight="0dip" + android:gravity="center_vertical" + android:prompt="@string/edit_event_calendar_label" /> + <ImageButton + android:id="@+id/change_color_new_event" + style="@style/EditEventColorImageButton" /> + </LinearLayout> </TableRow> <!-- CALENDAR DISPLAY for existing events --> @@ -57,13 +61,28 @@ <TextView android:text="@string/edit_event_calendar_label" style="@style/TextAppearance.EditEvent_Label" /> - <TextView - android:id="@+id/calendar_textview" - android:textColor="#FFFFFFFF" - android:minHeight="48dip" - android:paddingLeft="12dip" - android:paddingRight="12dip" - style="@style/TextAppearance.EditEvent_Value" /> + <LinearLayout + android:id="@+id/calendar_textview_with_colorpicker" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="12dip" + android:layout_marginRight="12dip" > + <TextView + android:id="@+id/calendar_textview" + style="@style/TextAppearance.EditEvent_Value" + android:layout_width="0dip" + android:layout_weight="1" + android:paddingLeft="12dip" + android:paddingRight="12dip" + android:layout_marginLeft="0dip" + android:layout_marginRight="0dip" + android:layout_height="wrap_content" + android:minHeight="48dip" + android:textColor="#FFFFFFFF" /> + <ImageButton + android:id="@+id/change_color_existing_event" + style="@style/EditEventColorImageButton" /> + </LinearLayout> </TableRow> <!-- WHAT --> diff --git a/res/layout/color_picker_dialog.xml b/res/layout/color_picker_dialog.xml new file mode 100644 index 00000000..44518c0f --- /dev/null +++ b/res/layout/color_picker_dialog.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 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. +--> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center" > + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center" + android:padding="28dp" > + + <ProgressBar + android:id="@android:id/progress" + style="?android:attr/progressBarStyleLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center" /> + + <com.android.calendar.color.ColorPickerPalette + android:id="@+id/color_picker" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center" + android:visibility="gone" /> + </FrameLayout> +</ScrollView>
\ No newline at end of file diff --git a/res/layout/edit_event_1.xml b/res/layout/edit_event_1.xml index 7242a384..46aaf525 100644 --- a/res/layout/edit_event_1.xml +++ b/res/layout/edit_event_1.xml @@ -22,7 +22,7 @@ <!-- CALENDARS SELECTOR for new events --> <LinearLayout android:id="@+id/calendar_selector_group" - android:orientation="vertical" + android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:focusable="true" > @@ -30,41 +30,54 @@ android:id="@+id/calendars_spinner" android:prompt="@string/edit_event_calendar_label" android:layout_height="wrap_content" - android:layout_width="match_parent" + android:layout_width="0dip" + android:layout_weight="1" android:layout_gravity="center_vertical" android:paddingBottom="10dip" android:paddingTop="10dip" android:layout_marginLeft="12dip" android:layout_marginRight="12dip" android:gravity="center_vertical" /> + <ImageButton + android:id="@+id/change_color_new_event" + style="@style/EditEventColorImageButton" /> </LinearLayout> <!-- CALENDAR DISPLAY for existing events --> <LinearLayout android:id="@+id/calendar_group" - android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="5dip" android:paddingTop="5dip" android:focusable="true"> - <TextView - android:id="@+id/calendar_textview" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:textColor="#FFFFFFFF" - android:layout_marginLeft="24dip" - android:layout_marginRight="24dip" - style="@style/TextAppearance.EditEvent_Value" /> - <TextView - android:id="@+id/calendar_textview_secondary" + <LinearLayout + android:layout_width="0dip" + android:layout_weight="1" android:layout_height="wrap_content" - android:layout_width="match_parent" - android:textColor="#FFFFFFFF" - android:layout_marginLeft="24dip" - android:layout_marginRight="24dip" - android:textSize="14sp" - style="@style/TextAppearance.EditEvent_Value" /> + android:focusable="true" + android:orientation="vertical" > + <TextView + android:id="@+id/calendar_textview" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:textColor="#FFFFFFFF" + android:layout_marginLeft="24dip" + android:layout_marginRight="24dip" + style="@style/TextAppearance.EditEvent_Value" /> + <TextView + android:id="@+id/calendar_textview_secondary" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:textColor="#FFFFFFFF" + android:layout_marginLeft="24dip" + android:layout_marginRight="24dip" + android:textSize="14sp" + style="@style/TextAppearance.EditEvent_Value" /> + </LinearLayout> + <ImageButton + android:id="@+id/change_color_existing_event" + style="@style/EditEventColorImageButton" /> </LinearLayout> <!-- WHAT --> diff --git a/res/layout/event_info.xml b/res/layout/event_info.xml index eb297fbf..8b8b5539 100644 --- a/res/layout/event_info.xml +++ b/res/layout/event_info.xml @@ -46,7 +46,7 @@ android:animateLayoutChanges="true" android:layout_height="match_parent"> - <LinearLayout + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> @@ -54,95 +54,7 @@ <!-- Container for the event's headline Name, Date, Time & Location --> - <LinearLayout - android:id="@+id/event_info_headline" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingLeft="16dip" - android:paddingRight="16dip" - android:paddingTop="8dip" - android:paddingBottom="16dip" - android:layout_weight="1" - android:orientation="vertical"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <!-- WHAT --> - <TextView - android:id="@+id/title" - android:layout_weight=".8" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:autoLink="all" - android:textIsSelectable="true" - android:textStyle="bold" - android:textColor="@color/event_info_headline_color" - android:textColorLink="@color/event_info_headline_color" - style="?android:attr/textAppearanceLarge" - android:textSize="24sp" /> - <!-- BUTTONS --> - <LinearLayout - android:id="@+id/event_info_buttons_container" - android:orientation="horizontal" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="right"> - <Button - android:id="@+id/edit" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" - android:enabled="false" - style="?android:attr/buttonBarButtonStyle" - android:textColor="@color/event_info_headline_color" - android:text="@string/edit_event_label" /> - <Button - android:id="@+id/delete" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" - style="?android:attr/buttonBarButtonStyle" - android:textColor="@color/event_info_headline_color" - android:text="@string/delete_label" /> - </LinearLayout> - </LinearLayout> - - <!-- WHEN --> - <TextView - android:id="@+id/when_datetime" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="4dip" - android:textIsSelectable="true" - android:textSize="14sp" - android:textColor="@color/event_info_headline_color" - style="?android:attr/textAppearanceLarge" /> - - <TextView - android:id="@+id/when_repeat" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="-3dip" - android:textSize="14sp" - android:textColor="@color/event_info_headline_transparent_color" - style="?android:attr/textAppearanceLarge" /> - - <!-- WHERE --> - <TextView - android:id="@+id/where" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ellipsize="end" - android:singleLine="false" - android:layout_marginTop="4dip" - android:textIsSelectable="true" - android:textSize="14sp" - android:textColor="@color/event_info_headline_color" - android:textColorLink="@color/event_info_headline_link_color" - style="?android:attr/textAppearanceLarge" /> - </LinearLayout> + <include layout="@layout/event_info_headline" /> <LinearLayout android:layout_width="match_parent" diff --git a/res/layout/event_info_dialog.xml b/res/layout/event_info_dialog.xml index e51b7f24..ff4d1c57 100644 --- a/res/layout/event_info_dialog.xml +++ b/res/layout/event_info_dialog.xml @@ -94,6 +94,10 @@ android:layout_marginRight="16dip" android:layout_gravity="right"> <ImageButton + android:id="@+id/change_color" + style="@style/EditEventColorImageButton" + android:visibility="gone" /> + <ImageButton android:id="@+id/edit" android:contentDescription="@string/edit_label" android:layout_width="48dip" diff --git a/res/layout/event_info_headline.xml b/res/layout/event_info_headline.xml new file mode 100644 index 00000000..26f97b6f --- /dev/null +++ b/res/layout/event_info_headline.xml @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2013 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. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/event_info_headline" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical" + android:paddingBottom="16dip" + android:paddingLeft="16dip" + android:paddingRight="16dip" + android:paddingTop="8dip" > + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" > + + <!-- WHAT --> + <TextView + android:id="@+id/title" + style="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight=".8" + android:autoLink="all" + android:textColor="@color/event_info_headline_color" + android:textColorLink="@color/event_info_headline_color" + android:textIsSelectable="true" + android:textSize="24sp" + android:textStyle="bold" /> + <!-- BUTTONS --> + + <LinearLayout + android:id="@+id/event_info_buttons_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="right" + android:orientation="horizontal" > + + <Button + android:id="@+id/change_color" + style="?android:attr/buttonBarButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:enabled="false" + android:text="@string/choose_event_color_label" + android:textColor="@color/event_info_headline_color" /> + + <Button + android:id="@+id/edit" + style="?android:attr/buttonBarButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:enabled="false" + android:text="@string/edit_event_label" + android:textColor="@color/event_info_headline_color" /> + + <Button + android:id="@+id/delete" + style="?android:attr/buttonBarButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/delete_label" + android:textColor="@color/event_info_headline_color" /> + </LinearLayout> + </LinearLayout> + + <!-- WHEN --> + + <TextView + android:id="@+id/when_datetime" + style="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dip" + android:textColor="@color/event_info_headline_color" + android:textIsSelectable="true" + android:textSize="14sp" /> + + <TextView + android:id="@+id/when_repeat" + style="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="-3dip" + android:textColor="@color/event_info_headline_transparent_color" + android:textSize="14sp" /> + + <!-- WHERE --> + + <TextView + android:id="@+id/where" + style="?android:attr/textAppearanceLarge" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dip" + android:ellipsize="end" + android:singleLine="false" + android:textColor="@color/event_info_headline_color" + android:textColorLink="@color/event_info_headline_link_color" + android:textIsSelectable="true" + android:textSize="14sp" /> + +</LinearLayout>
\ No newline at end of file diff --git a/res/menu/event_info_title_bar.xml b/res/menu/event_info_title_bar.xml index d2a3eec8..b3a6d074 100644 --- a/res/menu/event_info_title_bar.xml +++ b/res/menu/event_info_title_bar.xml @@ -16,6 +16,13 @@ <menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id='@+id/info_action_change_color' + android:alphabeticShortcut="c" + android:title="@string/choose_event_color_label" + android:icon="@drawable/ic_menu_colorpicker_holo_light" + android:showAsAction="withText|always" + android:enabled="false" + android:visible="false" /> <item android:id="@+id/info_action_edit" android:alphabeticShortcut="e" android:title="@string/edit_label" diff --git a/res/values/dimens.xml b/res/values/dimens.xml index d2caea0f..f0b57ee0 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -52,4 +52,9 @@ <dimen name="calendar_controls_height">300dip</dimen> <dimen name="agenda_item_right_margin">4dip</dimen> <dimen name="today_icon_text_size">14sp</dimen> + + <dimen name="color_swatch_large">64dip</dimen> + <dimen name="color_swatch_small">48dip</dimen> + <dimen name="color_swatch_margins_large">8dip</dimen> + <dimen name="color_swatch_margins_small">4dip</dimen> </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index aee89b91..a4ead66a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -278,6 +278,13 @@ <string name="view_event_organizer_label">Organizer:</string> <!-- Label for the local timezone --> + <!-- Label for showing event color picker --> + <string name="choose_event_color_label">Choose event color</string> + <!-- Title for event color picker dialog --> + <string name="event_color_picker_dialog_title">Event Color</string> + <!-- Label for button which sets event color to default calendar color --> + <string name="event_color_set_to_default">Set to default calendar color</string> + <!-- Label for whether the user is attending this event. This is shown when a user is invited to a meeting or event. The possible answers are 'yes', 'no', and 'maybe' (and, initially, 'no response'). --> diff --git a/res/values/styles.xml b/res/values/styles.xml index 4545a15f..0a3fd966 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -17,7 +17,7 @@ ** limitations under the License. */ --> -<resources> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> @@ -224,6 +224,19 @@ <item name="android:ellipsize">none</item> <item name="android:padding">4dp</item> </style> + + <style name="EditEventColorImageButton" parent="android:attr/buttonBarButtonStyle"> + <item name="android:enabled">false</item> + <item name="android:layout_width">48dip</item> + <item name="android:layout_height">48dip</item> + <item name="android:layout_marginRight">8dip</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:contentDescription">@string/choose_event_color_label</item> + <item name="android:padding">8dip</item> + <item name="android:scaleType">centerInside</item> + <item name="android:background">@android:color/transparent</item> + <item name="android:src">@drawable/ic_menu_colorpicker_holo_dark</item> + </style> <style name="WidgetDayOfWeekStyle"> <item name="android:layout_width">wrap_content</item> diff --git a/src/com/android/calendar/CalendarEventModel.java b/src/com/android/calendar/CalendarEventModel.java index 6da72806..44c66bf0 100644 --- a/src/com/android/calendar/CalendarEventModel.java +++ b/src/com/android/calendar/CalendarEventModel.java @@ -16,9 +16,6 @@ package com.android.calendar; -import com.android.calendar.event.EditEventHelper; -import com.android.common.Rfc822Validator; - import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -29,6 +26,10 @@ import android.provider.CalendarContract.Reminders; import android.text.TextUtils; import android.text.util.Rfc822Token; +import com.android.calendar.event.EditEventHelper; +import com.android.calendar.event.EventColorCache; +import com.android.common.Rfc822Validator; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -201,6 +202,8 @@ public class CalendarEventModel implements Serializable { public long mCalendarId = -1; public String mCalendarDisplayName = ""; // Make sure this is in sync with the mCalendarId public int mCalendarColor = 0; + public String mCalendarAccountName; + public String mCalendarAccountType; public int mCalendarMaxReminders; public String mCalendarAllowedReminders; public String mCalendarAllowedAttendeeTypes; @@ -210,6 +213,9 @@ public class CalendarEventModel implements Serializable { public String mSyncAccount = null; public String mSyncAccountType = null; + public EventColorCache mEventColorCache; + public int mEventColor = -1; + // PROVIDER_NOTES owner account comes from the calendars table public String mOwnerAccount = null; public String mTitle = null; @@ -843,6 +849,11 @@ public class CalendarEventModel implements Serializable { if (mEventStatus != originalModel.mEventStatus) { return false; } + + if (mEventColor != originalModel.mEventColor) { + return false; + } + return true; } @@ -872,4 +883,20 @@ public class CalendarEventModel implements Serializable { return true; } + + public int[] getCalendarEventColors() { + if (mEventColorCache != null) { + return mEventColorCache.getColorArray(mCalendarAccountName, mCalendarAccountType); + } + return null; + } + + public int getEventColorKey() { + if (mEventColorCache != null) { + return mEventColorCache.getColorKey(mCalendarAccountName, mCalendarAccountType, + mEventColor); + } + return -1; + } + } diff --git a/src/com/android/calendar/EventInfoActivity.java b/src/com/android/calendar/EventInfoActivity.java index 2b4c8a13..a0a126f6 100644 --- a/src/com/android/calendar/EventInfoActivity.java +++ b/src/com/android/calendar/EventInfoActivity.java @@ -25,8 +25,11 @@ import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Intent; import android.content.res.Resources; +import android.database.ContentObserver; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; +import android.provider.CalendarContract; import android.provider.CalendarContract.Attendees; import android.util.Log; import android.widget.Toast; @@ -42,6 +45,23 @@ public class EventInfoActivity extends Activity { private long mStartMillis, mEndMillis; private long mEventId; + // Create an observer so that we can update the views whenever a + // Calendar event changes. + private final ContentObserver mObserver = new ContentObserver(new Handler()) { + @Override + public boolean deliverSelfNotifications() { + return false; + } + + @Override + public void onChange(boolean selfChange) { + if (selfChange) return; + if (mInfoFragment != null) { + mInfoFragment.reloadEvents(); + } + } + }; + @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -135,53 +155,6 @@ public class EventInfoActivity extends Activity { } } -// @Override -// public boolean onOptionsItemSelected(MenuItem item) { -// -// // Handles option menu selections: -// // Home button - close event info activity and start the main calendar one -// // Edit button - start the event edit activity and close the info activity -// // Delete button - start a delete query that calls a runnable that close the info activity -// -// switch (item.getItemId()) { -// case android.R.id.home: -// Intent launchIntent = new Intent(); -// launchIntent.setAction(Intent.ACTION_VIEW); -// launchIntent.setData(Uri.parse(CalendarContract.CONTENT_URI + "/time")); -// launchIntent.setFlags( -// Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); -// startActivity(launchIntent); -// finish(); -// return true; -// case R.id.info_action_edit: -// Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, mEventId); -// Intent intent = new Intent(Intent.ACTION_EDIT, uri); -// intent.putExtra(EXTRA_EVENT_BEGIN_TIME, mStartMillis); -// intent.putExtra(EXTRA_EVENT_END_TIME, mEndMillis); -// intent.setClass(this, EditEventActivity.class); -// intent.putExtra(EVENT_EDIT_ON_LAUNCH, true); -// startActivity(intent); -// finish (); -// break; -// case R.id.info_action_delete: -// DeleteEventHelper deleteHelper = new DeleteEventHelper( -// this, this, true /* exitWhenDone */); -// deleteHelper.delete(mStartMillis, mEndMillis, mEventId, -1, onDeleteRunnable); -// break; -// default: -// break; -// } -// return super.onOptionsItemSelected(item); -// } - - // runs at the end of a delete action and closes the activity -// private Runnable onDeleteRunnable = new Runnable() { -// @Override -// public void run() { -// finish (); -// } -// }; - @Override protected void onNewIntent(Intent intent) { // From the Android Dev Guide: "It's important to note that when @@ -202,11 +175,14 @@ public class EventInfoActivity extends Activity { @Override protected void onResume() { super.onResume(); + getContentResolver().registerContentObserver(CalendarContract.Events.CONTENT_URI, + true, mObserver); } @Override protected void onPause() { super.onPause(); + getContentResolver().unregisterContentObserver(mObserver); } @Override diff --git a/src/com/android/calendar/EventInfoFragment.java b/src/com/android/calendar/EventInfoFragment.java index 05ef6e0d..a8d27f81 100644 --- a/src/com/android/calendar/EventInfoFragment.java +++ b/src/com/android/calendar/EventInfoFragment.java @@ -42,6 +42,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.database.Cursor; +import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -49,6 +50,7 @@ import android.os.Bundle; import android.provider.CalendarContract; import android.provider.CalendarContract.Attendees; import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Colors; import android.provider.CalendarContract.Events; import android.provider.CalendarContract.Reminders; import android.provider.ContactsContract; @@ -64,6 +66,7 @@ import android.text.method.MovementMethod; import android.text.style.ForegroundColorSpan; import android.text.util.Rfc822Token; import android.util.Log; +import android.util.SparseIntArray; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; @@ -94,9 +97,12 @@ import com.android.calendar.CalendarController.EventType; import com.android.calendar.CalendarEventModel.Attendee; import com.android.calendar.CalendarEventModel.ReminderEntry; import com.android.calendar.alerts.QuickResponseActivity; +import com.android.calendar.color.ColorComparator; +import com.android.calendar.color.ColorPickerSwatch.OnColorSelectedListener; import com.android.calendar.event.AttendeesView; import com.android.calendar.event.EditEventActivity; import com.android.calendar.event.EditEventHelper; +import com.android.calendar.event.EventColorPickerDialog; import com.android.calendar.event.EventViewUtils; import com.android.calendarcommon2.DateException; import com.android.calendarcommon2.Duration; @@ -110,7 +116,7 @@ import java.util.List; public class EventInfoFragment extends DialogFragment implements OnCheckedChangeListener, CalendarController.EventHandler, OnClickListener, DeleteEventHelper.DeleteNotifyListener { - public static final boolean DEBUG = false; + public static final boolean DEBUG = true; public static final String TAG = "EventInfoFragment"; @@ -122,8 +128,14 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange protected static final String BUNDLE_KEY_WINDOW_STYLE = "key_window_style"; protected static final String BUNDLE_KEY_ATTENDEE_RESPONSE = "key_attendee_response"; + protected static final String BUNDLE_KEY_CALENDAR_COLOR = "key_calendar_color"; + protected static final String BUNDLE_KEY_CURRENT_COLOR = "key_current_color"; + protected static final String BUNDLE_KEY_ORIGINAL_COLOR = "key_original_color"; + private static final String PERIOD_SPACE = ". "; + private static final String NO_EVENT_COLOR = ""; + /** * These are the corresponding indices into the array of strings * "R.array.change_response_labels" in the resource file. @@ -144,9 +156,11 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange private static final int TOKEN_QUERY_DUPLICATE_CALENDARS = 1 << 3; private static final int TOKEN_QUERY_REMINDERS = 1 << 4; private static final int TOKEN_QUERY_VISIBLE_CALENDARS = 1 << 5; + private static final int TOKEN_QUERY_COLORS = 1 << 6; + private static final int TOKEN_QUERY_ALL = TOKEN_QUERY_DUPLICATE_CALENDARS | TOKEN_QUERY_ATTENDEES | TOKEN_QUERY_CALENDARS | TOKEN_QUERY_EVENT - | TOKEN_QUERY_REMINDERS | TOKEN_QUERY_VISIBLE_CALENDARS; + | TOKEN_QUERY_REMINDERS | TOKEN_QUERY_VISIBLE_CALENDARS | TOKEN_QUERY_COLORS; private int mCurrentQuery = 0; @@ -162,17 +176,18 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange Events.DESCRIPTION, // 8 Events.EVENT_LOCATION, // 9 Calendars.CALENDAR_ACCESS_LEVEL, // 10 - Events.DISPLAY_COLOR, // 11 If SDK < 16, set to Calendars.CALENDAR_COLOR. - Events.HAS_ATTENDEE_DATA, // 12 - Events.ORGANIZER, // 13 - Events.HAS_ALARM, // 14 - Calendars.MAX_REMINDERS, //15 - Calendars.ALLOWED_REMINDERS, // 16 - Events.CUSTOM_APP_PACKAGE, // 17 - Events.CUSTOM_APP_URI, // 18 - Events.ORIGINAL_SYNC_ID, // 19 do not remove; used in DeleteEventHelper + Events.CALENDAR_COLOR, // 11 + Events.EVENT_COLOR, // 12 + Events.HAS_ATTENDEE_DATA, // 13 + Events.ORGANIZER, // 14 + Events.HAS_ALARM, // 15 + Calendars.MAX_REMINDERS, // 16 + Calendars.ALLOWED_REMINDERS, // 17 + Events.CUSTOM_APP_PACKAGE, // 18 + Events.CUSTOM_APP_URI, // 19 Events.DTEND, // 20 Events.DURATION, // 21 + Events.ORIGINAL_SYNC_ID // 22 do not remove; used in DeleteEventHelper }; private static final int EVENT_INDEX_ID = 0; private static final int EVENT_INDEX_TITLE = 1; @@ -185,14 +200,15 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange private static final int EVENT_INDEX_DESCRIPTION = 8; private static final int EVENT_INDEX_EVENT_LOCATION = 9; private static final int EVENT_INDEX_ACCESS_LEVEL = 10; - private static final int EVENT_INDEX_COLOR = 11; - private static final int EVENT_INDEX_HAS_ATTENDEE_DATA = 12; - private static final int EVENT_INDEX_ORGANIZER = 13; - private static final int EVENT_INDEX_HAS_ALARM = 14; - private static final int EVENT_INDEX_MAX_REMINDERS = 15; - private static final int EVENT_INDEX_ALLOWED_REMINDERS = 16; - private static final int EVENT_INDEX_CUSTOM_APP_PACKAGE = 17; - private static final int EVENT_INDEX_CUSTOM_APP_URI = 18; + private static final int EVENT_INDEX_CALENDAR_COLOR = 11; + private static final int EVENT_INDEX_EVENT_COLOR = 12; + private static final int EVENT_INDEX_HAS_ATTENDEE_DATA = 13; + private static final int EVENT_INDEX_ORGANIZER = 14; + private static final int EVENT_INDEX_HAS_ALARM = 15; + private static final int EVENT_INDEX_MAX_REMINDERS = 16; + private static final int EVENT_INDEX_ALLOWED_REMINDERS = 17; + private static final int EVENT_INDEX_CUSTOM_APP_PACKAGE = 18; + private static final int EVENT_INDEX_CUSTOM_APP_URI = 19; private static final int EVENT_INDEX_DTEND = 20; private static final int EVENT_INDEX_DURATION = 21; @@ -215,7 +231,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange static { if (!Utils.isJellybeanOrLater()) { - EVENT_PROJECTION[EVENT_INDEX_COLOR] = Calendars.CALENDAR_COLOR; EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_PACKAGE] = Events._ID; // dummy value EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_URI] = Events._ID; // dummy value @@ -245,17 +260,30 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange Calendars.CALENDAR_DISPLAY_NAME, // 1 Calendars.OWNER_ACCOUNT, // 2 Calendars.CAN_ORGANIZER_RESPOND, // 3 - Calendars.ACCOUNT_NAME // 4 + Calendars.ACCOUNT_NAME, // 4 + Calendars.ACCOUNT_TYPE // 5 }; static final int CALENDARS_INDEX_DISPLAY_NAME = 1; static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2; static final int CALENDARS_INDEX_OWNER_CAN_RESPOND = 3; static final int CALENDARS_INDEX_ACCOUNT_NAME = 4; + static final int CALENDARS_INDEX_ACCOUNT_TYPE = 5; static final String CALENDARS_WHERE = Calendars._ID + "=?"; static final String CALENDARS_DUPLICATE_NAME_WHERE = Calendars.CALENDAR_DISPLAY_NAME + "=?"; static final String CALENDARS_VISIBLE_WHERE = Calendars.VISIBLE + "=?"; + static final String[] COLORS_PROJECTION = new String[] { + Colors._ID, // 0 + Colors.COLOR, // 1 + Colors.COLOR_KEY // 2 + }; + + static final String COLORS_WHERE = Colors.ACCOUNT_NAME + "=? AND " + Colors.ACCOUNT_TYPE + + "=? AND " + Colors.COLOR_TYPE + "=" + Colors.TYPE_EVENT; + + public static final int COLORS_INDEX_COLOR = 1; + public static final int COLORS_INDEX_COLOR_KEY = 2; private View mView; @@ -312,20 +340,25 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange private View mLoadingMsgView; private ObjectAnimator mAnimateAlpha; private long mLoadingMsgStartTime; + + private EventColorPickerDialog mDialog; + private SparseIntArray mColorKeyMap = new SparseIntArray(); + private int[] mColors; + private int mOriginalColor = -1; + private int mCalendarColor = -1; + private int mCurrentColor = -1; + private static final int FADE_IN_TIME = 300; // in milliseconds private static final int LOADING_MSG_DELAY = 600; // in milliseconds private static final int LOADING_MSG_MIN_DISPLAY_TIME = 600; private boolean mNoCrossFade = false; // Used to prevent repeated cross-fade - ArrayList<Attendee> mAcceptedAttendees = new ArrayList<Attendee>(); ArrayList<Attendee> mDeclinedAttendees = new ArrayList<Attendee>(); ArrayList<Attendee> mTentativeAttendees = new ArrayList<Attendee>(); ArrayList<Attendee> mNoResponseAttendees = new ArrayList<Attendee>(); ArrayList<String> mToEmails = new ArrayList<String>(); ArrayList<String> mCcEmails = new ArrayList<String>(); - private int mColor; - private int mDefaultReminderMinutes; private final ArrayList<LinearLayout> mReminderViews = new ArrayList<LinearLayout>(0); @@ -386,6 +419,8 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange private Activity mActivity; private Context mContext; + private CalendarController mController; + private class QueryHandler extends AsyncQueryService { public QueryHandler(Context context) { super(context); @@ -412,6 +447,22 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange activity.finish(); return; } + if (mCalendarColor == -1) { + mCalendarColor = Utils.getDisplayColorFromColor( + mEventCursor.getInt(EVENT_INDEX_CALENDAR_COLOR)); + } + + if (mOriginalColor == -1) { + mOriginalColor = mEventCursor.isNull(EVENT_INDEX_EVENT_COLOR) + ? mCalendarColor : Utils.getDisplayColorFromColor( + mEventCursor.getInt(EVENT_INDEX_EVENT_COLOR)); + } + + Log.d(TAG, "Current event color: " + mCurrentColor); + if (mCurrentColor == -1) { + mCurrentColor = mOriginalColor; + } + updateEvent(mView); prepareReminders(); @@ -428,6 +479,13 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange // FRAG_TODO fragments shouldn't set the title anymore updateTitle(); + args = new String[] { + mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_NAME), + mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_TYPE) }; + uri = Colors.CONTENT_URI; + startQuery(TOKEN_QUERY_COLORS, null, uri, COLORS_PROJECTION, COLORS_WHERE, args, + null); + if (!mIsBusyFreeCalendar) { args = new String[] { Long.toString(mEventId) }; @@ -448,6 +506,37 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange sendAccessibilityEventIfQueryDone(TOKEN_QUERY_REMINDERS); } break; + case TOKEN_QUERY_COLORS: + ArrayList<Integer> colors = new ArrayList<Integer>(); + if (cursor.moveToFirst()) { + do + { + int colorKey = cursor.getInt(COLORS_INDEX_COLOR_KEY); + int rawColor = cursor.getInt(COLORS_INDEX_COLOR); + int displayColor = Utils.getDisplayColorFromColor(rawColor); + mColorKeyMap.put(displayColor, colorKey); + colors.add(displayColor); + } while (cursor.moveToNext()); + } + cursor.close(); + Integer[] sortedColors = new Integer[colors.size()]; + Arrays.sort(colors.toArray(sortedColors), new ColorComparator()); + mColors = new int[sortedColors.length]; + for (int i = 0; i < sortedColors.length; i++) { + mColors[i] = sortedColors[i].intValue(); + + float[] hsv = new float[3]; + Color.colorToHSV(mColors[i], hsv); + Log.d("Color", "H:" + hsv[0] + ",S:" + hsv[1] + ",V:" + hsv[2]); + } + View button = mView.findViewById(R.id.change_color); + if (button != null && mColors.length > 0) { + button.setEnabled(true); + button.setVisibility(View.VISIBLE); + } + updateMenu(); + cursor.close(); + break; case TOKEN_QUERY_ATTENDEES: mAttendeesCursor = Utils.matrixCursorFromCursor(cursor); initAttendeesCursor(mView); @@ -552,8 +641,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange public EventInfoFragment() { } - - public EventInfoFragment(Context context, long eventId, long startMillis, long endMillis, int attendeeResponse, boolean isDialog, int windowStyle) { this(context, ContentUris.withAppendedId(Events.CONTENT_URI, eventId), startMillis, @@ -653,9 +740,17 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange } @Override + public void onDetach() { + super.onDetach(); + mController.deregisterEventHandler(R.layout.event_info); + } + + @Override public void onAttach(Activity activity) { super.onAttach(activity); mActivity = activity; + mController = CalendarController.getInstance(mActivity); + mController.registerEventHandler(R.layout.event_info, this); mEditResponseHelper = new EditResponseHelper(activity); if (mAttendeeResponseFromIntent != Attendees.ATTENDEE_STATUS_NONE) { @@ -678,6 +773,9 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange mDeleteDialogVisible = savedInstanceState.getBoolean(BUNDLE_KEY_DELETE_DIALOG_VISIBLE,false); + mCalendarColor = savedInstanceState.getInt(BUNDLE_KEY_CALENDAR_COLOR); + mOriginalColor = savedInstanceState.getInt(BUNDLE_KEY_ORIGINAL_COLOR); + mCurrentColor = savedInstanceState.getInt(BUNDLE_KEY_CURRENT_COLOR); } if (mWindowStyle == DIALOG_WINDOW_STYLE) { @@ -692,7 +790,8 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange mWhere = (TextView) mView.findViewById(R.id.where); mDesc = (ExpandableTextView) mView.findViewById(R.id.description); mHeadlines = mView.findViewById(R.id.event_info_headline); - mLongAttendees = (AttendeesView)mView.findViewById(R.id.long_attendee_list); + mLongAttendees = (AttendeesView) mView.findViewById(R.id.long_attendee_list); + mIsTabletConfig = Utils.getConfigBool(mActivity, R.bool.tablet_config); if (mUri == null) { @@ -757,6 +856,18 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange } }); + b = mView.findViewById(R.id.change_color); + b.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (!mCanModifyCalendar) { + return; + } + + showEventColorPickerDialog(); + } + }); + // Hide Edit/Delete buttons if in full screen mode on a phone if (!mIsDialog && !mIsTabletConfig || mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) { mView.findViewById(R.id.event_info_buttons_container).setVisibility(View.GONE); @@ -923,9 +1034,11 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange outState.putInt(BUNDLE_KEY_WINDOW_STYLE, mWindowStyle); outState.putBoolean(BUNDLE_KEY_DELETE_DIALOG_VISIBLE, mDeleteDialogVisible); outState.putInt(BUNDLE_KEY_ATTENDEE_RESPONSE, mAttendeeResponseFromIntent); + outState.putInt(BUNDLE_KEY_CALENDAR_COLOR, mCalendarColor); + outState.putInt(BUNDLE_KEY_ORIGINAL_COLOR, mOriginalColor); + outState.putInt(BUNDLE_KEY_CURRENT_COLOR, mCurrentColor); } - @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); @@ -974,16 +1087,53 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange mDeleteHelper.setOnDismissListener(createDeleteOnDismissListener()); mDeleteDialogVisible = true; mDeleteHelper.delete(mStartMillis, mEndMillis, mEventId, -1, onDeleteRunnable); + } else if (itemId == R.id.info_action_change_color) { + showEventColorPickerDialog(); } return super.onOptionsItemSelected(item); } + private void showEventColorPickerDialog() { + if (mDialog == null) { + mDialog = new EventColorPickerDialog(mColors, mCurrentColor, mCalendarColor, + mIsTabletConfig); + mDialog.setOnColorSelectedListener(new OnColorSelectedListener() { + + @Override + public void onColorSelected(int color) { + updateCurrentEventColor(color); + } + }); + } + if (!mDialog.isAdded()) { + mDialog.show(getFragmentManager(), TAG); + } + } + + private boolean saveEventColor() { + if (mCurrentColor == mOriginalColor) { + return false; + } + + ContentValues values = new ContentValues(); + if (mCurrentColor != mCalendarColor) { + values.put(Events.EVENT_COLOR_KEY, mColorKeyMap.get(mCurrentColor)); + } else { + values.put(Events.EVENT_COLOR_KEY, NO_EVENT_COLOR); + } + Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, mEventId); + mHandler.startUpdate(mHandler.getNextToken(), null, uri, values, + null, null, Utils.UNDO_DELAY); + return true; + } + @Override public void onStop() { Activity act = getActivity(); if (!mEventDeletionStarted && act != null && !act.isChangingConfigurations()) { boolean responseSaved = saveResponse(); - if (saveReminders() || responseSaved) { + boolean eventColorSaved = saveEventColor(); + if (saveReminders() || responseSaved || eventColorSaved) { Toast.makeText(getActivity(), R.string.saving_event, Toast.LENGTH_SHORT).show(); } } @@ -1141,6 +1291,11 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange } } + private void updateCurrentEventColor(int color) { + mCurrentColor = color; + mHeadlines.setBackgroundColor(color); + } + private void updateEvent(View view) { if (mEventCursor == null || view == null) { return; @@ -1189,8 +1344,7 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange String rRule = mEventCursor.getString(EVENT_INDEX_RRULE); String eventTimezone = mEventCursor.getString(EVENT_INDEX_EVENT_TIMEZONE); - mColor = Utils.getDisplayColorFromColor(mEventCursor.getInt(EVENT_INDEX_COLOR)); - mHeadlines.setBackgroundColor(mColor); + updateCurrentEventColor(mCurrentColor); // What if (eventName != null) { @@ -1419,6 +1573,7 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange } private void updateCalendar(View view) { + mCalendarOwnerAccount = ""; if (mCalendarsCursor != null && mEventCursor != null) { mCalendarsCursor.moveToFirst(); @@ -1487,7 +1642,6 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange button.setVisibility(View.VISIBLE); } } - if ((!mIsDialog && !mIsTabletConfig || mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) && mMenu != null) { mActivity.invalidateOptionsMenu(); @@ -1507,6 +1661,7 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange } MenuItem delete = mMenu.findItem(R.id.info_action_delete); MenuItem edit = mMenu.findItem(R.id.info_action_edit); + MenuItem changeColor = mMenu.findItem(R.id.info_action_change_color); if (delete != null) { delete.setVisible(mCanModifyCalendar); delete.setEnabled(mCanModifyCalendar); @@ -1515,6 +1670,10 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange edit.setVisible(mCanModifyEvent); edit.setEnabled(mCanModifyEvent); } + if (changeColor != null && mColors != null && mColors.length > 0) { + changeColor.setVisible(mCanModifyCalendar); + changeColor.setEnabled(mCanModifyCalendar); + } } private void updateAttendees(View view) { @@ -1767,15 +1926,14 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange @Override public void handleEvent(EventInfo event) { - if (event.eventType == EventType.EVENTS_CHANGED && mHandler != null) { - // reload the data - reloadEvents(); - } + reloadEvents(); } public void reloadEvents() { - mHandler.startQuery(TOKEN_QUERY_EVENT, null, mUri, EVENT_PROJECTION, - null, null, null); + if (mHandler != null) { + mHandler.startQuery(TOKEN_QUERY_EVENT, null, mUri, EVENT_PROJECTION, + null, null, null); + } } @Override @@ -1946,6 +2104,4 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange mDialogWidth = (int)r.getDimension(R.dimen.event_info_dialog_width); mDialogHeight = (int)r.getDimension(R.dimen.event_info_dialog_height); } - - } diff --git a/src/com/android/calendar/SearchActivity.java b/src/com/android/calendar/SearchActivity.java index 4775b8d7..1c257599 100644 --- a/src/com/android/calendar/SearchActivity.java +++ b/src/com/android/calendar/SearchActivity.java @@ -191,7 +191,6 @@ public class SearchActivity extends Activity implements CalendarController.Event event.getResponse(), false, EventInfoFragment.DIALOG_WINDOW_STYLE); ft.replace(R.id.agenda_event_info, mEventInfoFragment); ft.commit(); - mController.registerEventHandler(R.id.agenda_event_info, mEventInfoFragment); } else { Intent intent = new Intent(Intent.ACTION_VIEW); Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, event.id); @@ -238,7 +237,6 @@ public class SearchActivity extends Activity implements CalendarController.Event ft.remove(mEventInfoFragment); ft.commit(); mEventInfoFragment = null; - mController.deregisterEventHandler(R.id.agenda_event_info); mCurrentEventId = -1; } } diff --git a/src/com/android/calendar/agenda/AgendaFragment.java b/src/com/android/calendar/agenda/AgendaFragment.java index 898fdb3a..ac8e4dad 100644 --- a/src/com/android/calendar/agenda/AgendaFragment.java +++ b/src/com/android/calendar/agenda/AgendaFragment.java @@ -290,7 +290,6 @@ public class AgendaFragment extends Fragment implements CalendarController.Event * @param fragmentManager */ public void removeFragments(FragmentManager fragmentManager) { - mController.deregisterEventHandler(R.id.agenda_event_info); if (getActivity().isFinishing()) { return; } @@ -430,8 +429,6 @@ public class AgendaFragment extends Fragment implements CalendarController.Event Attendees.ATTENDEE_STATUS_NONE, false, EventInfoFragment.DIALOG_WINDOW_STYLE); ft.replace(R.id.agenda_event_info, mEventFragment); - mController.registerEventHandler(R.id.agenda_event_info, - mEventFragment); ft.commit(); } else { fOld.reloadEvents(); diff --git a/src/com/android/calendar/color/ColorComparator.java b/src/com/android/calendar/color/ColorComparator.java new file mode 100644 index 00000000..f0e5764c --- /dev/null +++ b/src/com/android/calendar/color/ColorComparator.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 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.color; + +import android.graphics.Color; + +import java.util.Comparator; + +/** + * A color comparator which compares based on hue, saturation, and value. + */ +public class ColorComparator implements Comparator<Integer> { + + @Override + public int compare(Integer lhs, Integer rhs) { + float[] hsv = new float[3]; + Color.colorToHSV(lhs, hsv); + float hue1 = hsv[0]; + float sat1 = hsv[1]; + float val1 = hsv[2]; + + float[] hsv2 = new float[3]; + Color.colorToHSV(rhs, hsv2); + float hue2 = hsv2[0]; + float sat2 = hsv2[1]; + float val2 = hsv2[2]; + + if (hue1 < hue2) { + return 1; + } else if (hue1 > hue2) { + return -1; + } else { + if (sat1 < sat2) { + return 1; + } else if (sat1 > sat2) { + return -1; + } else { + if (val1 < val2) { + return 1; + } else if (val1 > val2) { + return -1; + } + } + } + return 0; + } +} diff --git a/src/com/android/calendar/color/ColorPickerDialog.java b/src/com/android/calendar/color/ColorPickerDialog.java new file mode 100644 index 00000000..3b3d7f4c --- /dev/null +++ b/src/com/android/calendar/color/ColorPickerDialog.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2013 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.color; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.Context; +import android.graphics.Color; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ProgressBar; + +import com.android.calendar.R; +import com.android.calendar.color.ColorPickerSwatch.OnColorSelectedListener; + +/** + * A dialog which takes in as input an array of colors and creates a palette allowing the user to + * select a specific color swatch, which invokes a listener. + */ +public class ColorPickerDialog extends DialogFragment implements OnColorSelectedListener { + + public static final int SIZE_LARGE = 1; + public static final int SIZE_SMALL = 2; + + protected AlertDialog mAlertDialog; + + private static final String KEY_COLORS = "colors"; + private static final String KEY_CURRENT_COLOR = "current_color"; + private static final String KEY_COLUMNS = "columns"; + private static final String KEY_SIZE = "size"; + + protected String mTitle; + protected int mTitleResId; + protected int[] mColors; + protected int mSelectedColor; + protected int mColumns; + protected int mSize; + private ColorPickerPalette mPalette; + private ProgressBar mProgress; + + protected OnColorSelectedListener mListener; + + public ColorPickerDialog() { + // Empty constructor required for dialog fragments. + } + + public ColorPickerDialog(int titleResId, int[] colors, int selectedColor, + int columns, int size) { + mTitleResId = titleResId; + mColors = colors; + mSelectedColor = selectedColor; + mColumns = columns; + mSize = size; + } + + public void setOnColorSelectedListener(OnColorSelectedListener listener) { + mListener = listener; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + final LayoutInflater layoutInflater = (LayoutInflater) activity + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + if (savedInstanceState != null) { + mColors = (int[]) savedInstanceState.getSerializable(KEY_COLORS); + mSelectedColor = savedInstanceState.getInt(KEY_CURRENT_COLOR); + mColumns = savedInstanceState.getInt(KEY_COLUMNS); + mSize = savedInstanceState.getInt(KEY_SIZE); + } + + View view = LayoutInflater.from(getActivity()).inflate(R.layout.color_picker_dialog, null); + mProgress = (ProgressBar) view.findViewById(android.R.id.progress); + mPalette = (ColorPickerPalette) view.findViewById(R.id.color_picker); + mPalette.init(mSize, mColumns, this); + + if (mColors != null) { + showPalette(); + } + + if (mTitle == null) { + mTitle = activity.getString(mTitleResId); + } + + mAlertDialog = new AlertDialog.Builder(activity) + .setTitle(mTitle) + .setView(view) + .create(); + + return mAlertDialog; + } + + @Override + public void onColorSelected(int color) { + if (mListener != null) { + mListener.onColorSelected(color); + } + mSelectedColor = color; + dismiss(); + } + + public void showPalette() { + mProgress.setVisibility(View.GONE); + mPalette.setVisibility(View.VISIBLE); + mPalette.drawPalette(mColors, mSelectedColor); + } + + public void showProgress() { + mProgress.setVisibility(View.VISIBLE); + mPalette.setVisibility(View.GONE); + } + + public void setColors(int[] colors) { + if (colors == null) { + return; + } + mColors = colors; + showPalette(); + } + + public void setColors(int[] colors, int selectedColor) { + mSelectedColor = selectedColor; + setColors(colors); + } + + public void setSelectedColor(int color) { + if (mSelectedColor != color) { + setColors(mColors, color); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putSerializable(KEY_COLORS, mColors); + outState.putInt(KEY_CURRENT_COLOR, mSelectedColor); + outState.putInt(KEY_COLUMNS, mColumns); + outState.putInt(KEY_SIZE, mSize); + } + + @Override + public void onDestroyView() { + if (getDialog() != null && getRetainInstance()) { + getDialog().setDismissMessage(null); + } + super.onDestroyView(); + } +} diff --git a/src/com/android/calendar/color/ColorPickerPalette.java b/src/com/android/calendar/color/ColorPickerPalette.java new file mode 100644 index 00000000..dba1eda6 --- /dev/null +++ b/src/com/android/calendar/color/ColorPickerPalette.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2013 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.color; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TableLayout; +import android.widget.TableRow; + +import com.android.calendar.R; +import com.android.calendar.color.ColorPickerSwatch.OnColorSelectedListener; + +/** + * A color picker custom view which creates an grid of color squares. The number of squares per + * row (and the padding between the squares) is determined by the user. + */ +public class ColorPickerPalette extends TableLayout { + + public OnColorSelectedListener mOnColorSelectedListener; + + private int mSwatchLength; + private int mMarginSize; + private int mNumColumns; + + public ColorPickerPalette(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ColorPickerPalette(Context context) { + super(context); + } + + /** + * Initialize the size, columns, and listener. Size should be a pre-defined size (SIZE_LARGE + * or SIZE_SMALL) from ColorPickerDialogFragment. + */ + public void init(int size, int columns, OnColorSelectedListener listener) { + mNumColumns = columns; + if (size == ColorPickerDialog.SIZE_LARGE) { + mSwatchLength = getResources().getDimensionPixelSize(R.dimen.color_swatch_large); + mMarginSize = getResources().getDimensionPixelSize(R.dimen.color_swatch_margins_large); + } else { + mSwatchLength = getResources().getDimensionPixelSize(R.dimen.color_swatch_small); + mMarginSize = getResources().getDimensionPixelSize(R.dimen.color_swatch_margins_small); + } + mOnColorSelectedListener = listener; + } + + private TableRow createTableRow() { + TableRow row = new TableRow(getContext()); + ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + row.setLayoutParams(params); + return row; + } + + /** + * Adds swatches to table in a serpentine format. + */ + public void drawPalette(int[] colors, int selectedColor) { + + if (colors == null) { + return; + } + + this.removeAllViews(); + int rowElements = 0; + int rowNumber = 0; + + // Fills the table with swatches based on the array of colors. + TableRow row = createTableRow(); + for (int color : colors) { + addSwatchToRow(row, createColorSwatch(color, selectedColor), rowNumber); + rowElements++; + if (rowElements == mNumColumns) { + addView(row); + row = createTableRow(); + rowElements = 0; + rowNumber++; + } + } + + // Create blank views to fill the row if the last row has not been filled. + if (rowElements > 0) { + while (rowElements != mNumColumns) { + addSwatchToRow(row, createBlankSpace(), rowNumber); + rowElements++; + } + addView(row); + } + } + + /** + * Appends a swatch to the end of the row for even-numbered rows (starting with row 0), + * to the beginning of a row for odd-numbered rows. + */ + private void addSwatchToRow(TableRow row, View swatch, int rowNumber) { + if (rowNumber % 2 == 0) { + row.addView(swatch); + } else { + row.addView(swatch, 0); + } + } + + /** + * Creates a blank space to fill the row. + */ + private ImageView createBlankSpace() { + ImageView view = new ImageView(getContext()); + TableRow.LayoutParams params = new TableRow.LayoutParams(mSwatchLength, mSwatchLength); + params.setMargins(mMarginSize, mMarginSize, mMarginSize, mMarginSize); + view.setLayoutParams(params); + return view; + } + + /** + * Creates a color swatch. + */ + private ColorPickerSwatch createColorSwatch(int color, int selectedColor) { + ColorPickerSwatch view = new ColorPickerSwatch(getContext(), color, + color == selectedColor, mOnColorSelectedListener); + TableRow.LayoutParams params = new TableRow.LayoutParams(mSwatchLength, mSwatchLength); + params.setMargins(mMarginSize, mMarginSize, mMarginSize, mMarginSize); + view.setLayoutParams(params); + return view; + } +} diff --git a/src/com/android/calendar/color/ColorPickerSwatch.java b/src/com/android/calendar/color/ColorPickerSwatch.java new file mode 100644 index 00000000..c670116d --- /dev/null +++ b/src/com/android/calendar/color/ColorPickerSwatch.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2013 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.color; + +import android.content.Context; +import android.graphics.PorterDuff.Mode; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.ImageView; + +import com.android.calendar.R; + +import java.io.Serializable; + +/** + * Creates a circular swatch of a specified color. Adds a checkmark if marked as checked. + */ +public class ColorPickerSwatch extends ImageView implements View.OnClickListener { + + private int mColor; + private Drawable mColorDrawable; + private Drawable mCheckmark; + private OnColorSelectedListener mOnColorSelectedListener; + + /** + * Interface for a callback when a color square is selected. + */ + public interface OnColorSelectedListener { + + /** + * Called when a specific color square has been selected. + */ + public void onColorSelected(int color); + } + + /** + * @param context + */ + public ColorPickerSwatch(Context context) { + super(context); + } + + public ColorPickerSwatch(Context context, int color, boolean checked, + OnColorSelectedListener listener) { + super(context); + setScaleType(ScaleType.FIT_XY); + mColorDrawable = getContext().getResources() + .getDrawable(R.drawable.color_picker_swatch); + mCheckmark = getContext().getResources().getDrawable( + R.drawable.ic_colorpicker_swatch_selected); + mOnColorSelectedListener = listener; + setColor(color); + setChecked(checked); + setOnClickListener(this); + } + + protected void setColor(int color) { + mColor = color; + mColorDrawable.setColorFilter(color, Mode.SRC_ATOP); + setBackgroundDrawable(mColorDrawable); + } + + private void setChecked(boolean checked) { + if (checked) { + setImageDrawable(mCheckmark); + } else { + setImageDrawable(null); + } + } + + @Override + public void onClick(View v) { + if (mOnColorSelectedListener != null) { + mOnColorSelectedListener.onColorSelected(mColor); + } + } +} diff --git a/src/com/android/calendar/event/EditEventFragment.java b/src/com/android/calendar/event/EditEventFragment.java index a7aa2b43..a81672a6 100644 --- a/src/com/android/calendar/event/EditEventFragment.java +++ b/src/com/android/calendar/event/EditEventFragment.java @@ -35,6 +35,7 @@ import android.net.Uri; import android.os.Bundle; import android.provider.CalendarContract.Attendees; import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Colors; import android.provider.CalendarContract.Events; import android.provider.CalendarContract.Reminders; import android.text.TextUtils; @@ -61,6 +62,8 @@ import com.android.calendar.CalendarEventModel.ReminderEntry; import com.android.calendar.DeleteEventHelper; import com.android.calendar.R; import com.android.calendar.Utils; +import com.android.calendar.color.ColorComparator; +import com.android.calendar.color.ColorPickerSwatch.OnColorSelectedListener; import java.io.Serializable; import java.util.ArrayList; @@ -81,8 +84,10 @@ public class EditEventFragment extends Fragment implements EventHandler { private static final int TOKEN_ATTENDEES = 1 << 1; private static final int TOKEN_REMINDERS = 1 << 2; private static final int TOKEN_CALENDARS = 1 << 3; + private static final int TOKEN_COLORS = 1 << 4; + private static final int TOKEN_ALL = TOKEN_EVENT | TOKEN_ATTENDEES | TOKEN_REMINDERS - | TOKEN_CALENDARS; + | TOKEN_CALENDARS | TOKEN_COLORS; private static final int TOKEN_UNITIALIZED = 1 << 31; /** @@ -109,6 +114,8 @@ public class EditEventFragment extends Fragment implements EventHandler { private long mEnd; private long mCalendarId = -1; + private EventColorPickerDialog mDialog; + private Activity mContext; private final Done mOnDone = new Done(); @@ -303,12 +310,41 @@ public class EditEventFragment extends Fragment implements EventHandler { EditEventHelper.setModelFromCalendarCursor(mModel, cursor); EditEventHelper.setModelFromCalendarCursor(mOriginalModel, cursor); } + startQuery(TOKEN_COLORS, null, Colors.CONTENT_URI, + EditEventHelper.COLORS_PROJECTION, + Colors.COLOR_TYPE + "=" + Colors.TYPE_EVENT, null, null); } finally { cursor.close(); } - setModelIfDone(TOKEN_CALENDARS); break; + case TOKEN_COLORS: + if (cursor.moveToFirst()) { + EventColorCache cache = new EventColorCache(); + do + { + int colorKey = cursor.getInt(EditEventHelper.COLORS_INDEX_COLOR_KEY); + int rawColor = cursor.getInt(EditEventHelper.COLORS_INDEX_COLOR); + int displayColor = Utils.getDisplayColorFromColor(rawColor); + String accountName = cursor + .getString(EditEventHelper.COLORS_INDEX_ACCOUNT_NAME); + String accountType = cursor + .getString(EditEventHelper.COLORS_INDEX_ACCOUNT_TYPE); + cache.insertColor(accountName, accountType, + displayColor, colorKey); + } while (cursor.moveToNext()); + cache.sortPalettes(new ColorComparator()); + + mModel.mEventColorCache = cache; + mView.mColorPickerNewEvent.setOnClickListener(mOnColorPickerClicked); + mView.mColorPickerExistingEvent.setOnClickListener(mOnColorPickerClicked); + mView.setColorPickerButtonStates(mModel.getCalendarEventColors()); + } + if (cursor != null) { + cursor.close(); + } + setModelIfDone(TOKEN_COLORS); + break; default: cursor.close(); break; @@ -316,6 +352,35 @@ public class EditEventFragment extends Fragment implements EventHandler { } } + private View.OnClickListener mOnColorPickerClicked = new View.OnClickListener() { + + @Override + public void onClick(View v) { + int[] colors = mModel.getCalendarEventColors(); + if (mDialog == null) { + mDialog = new EventColorPickerDialog(colors, mModel.mEventColor, + mModel.mCalendarColor, mView.mIsMultipane); + mDialog.setOnColorSelectedListener(new OnColorSelectedListener() { + + @Override + public void onColorSelected(int color) { + // TODO(kingkung): Auto-generated method stub + if (mModel.mEventColor != color) { + mModel.mEventColor = color; + mView.updateHeadlineColor(mModel, color); + } + } + }); + } else { + mDialog.setCalendarColor(mModel.mCalendarColor); + mDialog.setColors(colors, mModel.mEventColor); + } + if (!mDialog.isAdded()) { + mDialog.show(getFragmentManager(), TAG); + } + } + }; + private void setModelIfDone(int queryType) { synchronized (this) { mOutstandingQueries &= ~queryType; diff --git a/src/com/android/calendar/event/EditEventHelper.java b/src/com/android/calendar/event/EditEventHelper.java index 49bd744d..32663e01 100644 --- a/src/com/android/calendar/event/EditEventHelper.java +++ b/src/com/android/calendar/event/EditEventHelper.java @@ -25,6 +25,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.provider.CalendarContract.Attendees; import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Colors; import android.provider.CalendarContract.Events; import android.provider.CalendarContract.Reminders; import android.text.TextUtils; @@ -59,6 +60,8 @@ public class EditEventHelper { private static final boolean DEBUG = false; + private static final String NO_EVENT_COLOR = ""; + public static final String[] EVENT_PROJECTION = new String[] { Events._ID, // 0 Events.TITLE, // 1 @@ -82,6 +85,9 @@ public class EditEventHelper { Events.GUESTS_CAN_MODIFY, // 19 Events.ORIGINAL_ID, // 20 Events.STATUS, // 21 + Events.CALENDAR_COLOR, // 22 + Events.EVENT_COLOR, // 23 + Events.EVENT_COLOR_KEY // 24 }; protected static final int EVENT_INDEX_ID = 0; protected static final int EVENT_INDEX_TITLE = 1; @@ -105,6 +111,9 @@ public class EditEventHelper { protected static final int EVENT_INDEX_GUESTS_CAN_MODIFY = 19; protected static final int EVENT_INDEX_ORIGINAL_ID = 20; protected static final int EVENT_INDEX_EVENT_STATUS = 21; + protected static final int EVENT_INDEX_CALENDAR_COLOR = 22; + protected static final int EVENT_INDEX_EVENT_COLOR = 23; + protected static final int EVENT_INDEX_EVENT_COLOR_KEY = 24; public static final String[] REMINDERS_PROJECTION = new String[] { Reminders._ID, // 0 @@ -191,6 +200,22 @@ public class EditEventHelper { static final String CALENDARS_WHERE = Calendars._ID + "=?"; + static final String[] COLORS_PROJECTION = new String[] { + Colors._ID, // 0 + Colors.ACCOUNT_NAME, + Colors.ACCOUNT_TYPE, + Colors.COLOR, // 1 + Colors.COLOR_KEY // 2 + }; + + static final String COLORS_WHERE = Colors.ACCOUNT_NAME + "=? AND " + Colors.ACCOUNT_TYPE + + "=? AND " + Colors.COLOR_TYPE + "=" + Colors.TYPE_EVENT; + + static final int COLORS_INDEX_ACCOUNT_NAME = 1; + static final int COLORS_INDEX_ACCOUNT_TYPE = 2; + static final int COLORS_INDEX_COLOR = 3; + static final int COLORS_INDEX_COLOR_KEY = 4; + static final String[] ATTENDEES_PROJECTION = new String[] { Attendees._ID, // 0 Attendees.ATTENDEE_NAME, // 1 @@ -1044,6 +1069,14 @@ public class EditEventHelper { model.mIsOrganizer = model.mOwnerAccount.equalsIgnoreCase(model.mOrganizer); model.mGuestsCanModify = cursor.getInt(EVENT_INDEX_GUESTS_CAN_MODIFY) != 0; + int rawEventColor; + if (cursor.isNull(EVENT_INDEX_EVENT_COLOR)) { + rawEventColor = cursor.getInt(EVENT_INDEX_CALENDAR_COLOR); + } else { + rawEventColor = cursor.getInt(EVENT_INDEX_EVENT_COLOR); + } + model.mEventColor = Utils.getDisplayColorFromColor(rawEventColor); + if (accessLevel > 0) { // For now the array contains the values 0, 2, and 3. We subtract // one to make it easier to handle in code as 0,1,2. @@ -1100,7 +1133,11 @@ public class EditEventHelper { model.mCalendarAccessLevel = cursor.getInt(CALENDARS_INDEX_ACCESS_LEVEL); model.mCalendarDisplayName = cursor.getString(CALENDARS_INDEX_DISPLAY_NAME); - model.mCalendarColor = cursor.getInt(CALENDARS_INDEX_COLOR); + model.mCalendarColor = Utils.getDisplayColorFromColor( + cursor.getInt(CALENDARS_INDEX_COLOR)); + + model.mCalendarAccountName = cursor.getString(CALENDARS_INDEX_ACCOUNT_NAME); + model.mCalendarAccountType = cursor.getString(CALENDARS_INDEX_ACCOUNT_TYPE); model.mCalendarMaxReminders = cursor.getInt(CALENDARS_INDEX_MAX_REMINDERS); model.mCalendarAllowedReminders = cursor.getString(CALENDARS_INDEX_ALLOWED_REMINDERS); @@ -1248,6 +1285,11 @@ public class EditEventHelper { } values.put(Events.ACCESS_LEVEL, accessLevel); values.put(Events.STATUS, model.mEventStatus); + if (model.mEventColor == -1 || model.mEventColor == model.mCalendarColor) { + values.put(Events.EVENT_COLOR_KEY, NO_EVENT_COLOR); + } else { + values.put(Events.EVENT_COLOR_KEY, model.getEventColorKey()); + } return values; } diff --git a/src/com/android/calendar/event/EditEventView.java b/src/com/android/calendar/event/EditEventView.java index 1c7b910e..1f9ba9fe 100644 --- a/src/com/android/calendar/event/EditEventView.java +++ b/src/com/android/calendar/event/EditEventView.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.DatePickerDialog; import android.app.DatePickerDialog.OnDateSetListener; +import android.app.FragmentManager; import android.app.ProgressDialog; import android.app.Service; import android.app.TimePickerDialog; @@ -31,11 +32,11 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.database.Cursor; import android.graphics.drawable.Drawable; +import android.provider.CalendarContract; import android.provider.CalendarContract.Attendees; import android.provider.CalendarContract.Calendars; import android.provider.CalendarContract.Events; import android.provider.CalendarContract.Reminders; -import android.provider.CalendarContract; import android.provider.Settings; import android.text.InputFilter; import android.text.TextUtils; @@ -47,6 +48,7 @@ import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -60,6 +62,7 @@ import android.widget.CalendarView; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.DatePicker; +import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.MultiAutoCompleteTextView; import android.widget.RadioButton; @@ -82,6 +85,7 @@ import com.android.calendar.RecipientAdapter; import com.android.calendar.TimezoneAdapter; import com.android.calendar.TimezoneAdapter.TimezoneRow; import com.android.calendar.Utils; +import com.android.calendar.color.ColorPickerSwatch.OnColorSelectedListener; import com.android.calendar.event.EditEventHelper.EditDoneRunnable; import com.android.calendarcommon2.EventRecurrence; import com.android.common.Rfc822InputFilter; @@ -115,6 +119,9 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa Button mStartTimeButton; Button mEndTimeButton; Button mTimezoneButton; + View mColorPickerNewEvent; + View mColorPickerExistingEvent; + OnClickListener mChangeColorOnClickListener; View mTimezoneRow; TextView mStartTimeHome; TextView mStartDateHome; @@ -150,7 +157,7 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa private int[] mOriginalPadding = new int[4]; private int[] mOriginalSpinnerPadding = new int[4]; - private boolean mIsMultipane; + public boolean mIsMultipane; private ProgressDialog mLoadingCalendarsDialog; private AlertDialog mNoCalendarsDialog; private AlertDialog mTimezoneDialog; @@ -868,6 +875,9 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa mEndHomeGroup = view.findViewById(R.id.to_row_home_tz); mAttendeesList = (MultiAutoCompleteTextView) view.findViewById(R.id.attendees); + mColorPickerNewEvent = view.findViewById(R.id.change_color_new_event); + mColorPickerExistingEvent = view.findViewById(R.id.change_color_existing_event); + mTitleTextView.setTag(mTitleTextView.getBackground()); mLocationTextView.setTag(mLocationTextView.getBackground()); mLocationAdapter = new EventLocationAdapter(activity); @@ -1056,6 +1066,9 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa boolean canRespond = EditEventHelper.canRespond(model); + final long eventId = model.mId; + final long calendarId = model.mCalendarId; + long begin = model.mStart; long end = model.mEnd; mTimezone = model.mTimezone; // this will be UTC for all day events @@ -1176,7 +1189,6 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa mResponseGroup.setVisibility(View.GONE); } - int displayColor = Utils.getDisplayColorFromColor(model.mCalendarColor); if (model.mUri != null) { // This is an existing event so hide the calendar spinner // since we can't change the calendar. @@ -1188,15 +1200,11 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa if (tv != null) { tv.setText(model.mOwnerAccount); } - if (mIsMultipane) { - mView.findViewById(R.id.calendar_textview).setBackgroundColor(displayColor); - } else { - mView.findViewById(R.id.calendar_group).setBackgroundColor(displayColor); - } } else { View calendarGroup = mView.findViewById(R.id.calendar_group); calendarGroup.setVisibility(View.GONE); } + updateHeadlineColor(model, model.mEventColor); populateWhen(); populateRepeats(); @@ -1208,6 +1216,23 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa sendAccessibilityEvent(); } + public void updateHeadlineColor(CalendarEventModel model, int displayColor) { + if (model.mUri != null) { + if (mIsMultipane) { + mView.findViewById(R.id.calendar_textview_with_colorpicker) + .setBackgroundColor(displayColor); + } else { + mView.findViewById(R.id.calendar_group).setBackgroundColor(displayColor); + } + } else { + if (mIsMultipane) { + mCalendarSelectorWrapper.setBackgroundColor(displayColor); + } else { + mCalendarSelectorGroup.setBackgroundColor(displayColor); + } + } + } + private void sendAccessibilityEvent() { AccessibilityManager am = (AccessibilityManager) mActivity.getSystemService(Service.ACCESSIBILITY_SERVICE); @@ -1655,6 +1680,16 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa updateHomeTime(); } + public void setColorPickerButtonStates(int[] eventColors) { + if (eventColors == null || eventColors.length == 0) { + mColorPickerNewEvent.setEnabled(false); + mColorPickerExistingEvent.setEnabled(false); + } else { + mColorPickerNewEvent.setEnabled(true); + mColorPickerExistingEvent.setEnabled(true); + } + } + @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { // This is only used for the Calendar spinner in new events, and only fires when the @@ -1666,6 +1701,13 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa return; } + // Do nothing if the selection didn't change so that reminders will not get lost + int idColumn = c.getColumnIndexOrThrow(Calendars._ID); + long calendarId = c.getLong(idColumn); + if (calendarId == mModel.mCalendarId) { + return; + } + int colorColumn = c.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR); int color = c.getInt(colorColumn); int displayColor = Utils.getDisplayColorFromColor(color); @@ -1676,14 +1718,14 @@ public class EditEventView implements View.OnClickListener, DialogInterface.OnCa mCalendarSelectorGroup.setBackgroundColor(displayColor); } - // Do nothing if the selection didn't change so that reminders will not get lost - int idColumn = c.getColumnIndexOrThrow(Calendars._ID); - long calendarId = c.getLong(idColumn); - if (calendarId == mModel.mCalendarId) { - return; - } mModel.mCalendarId = calendarId; - mModel.mCalendarColor = color; + mModel.mCalendarColor = displayColor; + mModel.mCalendarAccountName = c.getString(EditEventHelper.CALENDARS_INDEX_ACCOUNT_NAME); + mModel.mCalendarAccountType = c.getString(EditEventHelper.CALENDARS_INDEX_ACCOUNT_TYPE); + mModel.mEventColor = mModel.mCalendarColor; + + setColorPickerButtonStates(mModel.getCalendarEventColors()); + // Update the max/allowed reminders with the new calendar properties. int maxRemindersColumn = c.getColumnIndexOrThrow(Calendars.MAX_REMINDERS); mModel.mCalendarMaxReminders = c.getInt(maxRemindersColumn); diff --git a/src/com/android/calendar/event/EventColorCache.java b/src/com/android/calendar/event/EventColorCache.java new file mode 100644 index 00000000..e7ed6687 --- /dev/null +++ b/src/com/android/calendar/event/EventColorCache.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2013 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.event; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; + +/** + * A cache for event colors and event color keys stored based upon calendar account name and type. + */ +public class EventColorCache { + + private static final String SEPARATOR = "::"; + + private Map<String, ArrayList<Integer>> mColorPaletteMap; + private Map<String, Integer> mColorKeyMap; + + public EventColorCache() { + mColorPaletteMap = new HashMap<String, ArrayList<Integer>>(); + mColorKeyMap = new HashMap<String, Integer>(); + } + + /** + * Inserts a color into the cache. + */ + public void insertColor(String accountName, String accountType, int displayColor, + int colorKey) { + mColorKeyMap.put(createKey(accountName, accountType, displayColor), colorKey); + String key = createKey(accountName, accountType); + ArrayList<Integer> colorPalette; + if ((colorPalette = mColorPaletteMap.get(key)) == null) { + colorPalette = new ArrayList<Integer>(); + } + colorPalette.add(displayColor); + mColorPaletteMap.put(key, colorPalette); + } + + /** + * Retrieve an array of colors for a specific account name and type. + */ + public int[] getColorArray(String accountName, String accountType) { + ArrayList<Integer> colors = mColorPaletteMap.get(createKey(accountName, accountType)); + if (colors == null) { + return null; + } + int[] ret = new int[colors.size()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = colors.get(i); + } + return ret; + } + + /** + * Retrieve an event color's unique key based on account name, type, and color. + */ + public int getColorKey(String accountName, String accountType, int displayColor) { + return mColorKeyMap.get(createKey(accountName, accountType, displayColor)); + } + + /** + * Sorts the arrays of colors based on a comparator. + */ + public void sortPalettes(Comparator<Integer> comparator) { + for (String key : mColorPaletteMap.keySet()) { + ArrayList<Integer> palette = mColorPaletteMap.get(key); + Integer[] sortedColors = new Integer[palette.size()]; + Arrays.sort(palette.toArray(sortedColors), comparator); + palette.clear(); + for (Integer color : sortedColors) { + palette.add(color); + } + mColorPaletteMap.put(key, palette); + } + } + + private String createKey(String accountName, String accountType) { + return new StringBuilder().append(accountName) + .append(SEPARATOR) + .append(accountType) + .toString(); + } + + private String createKey(String accountName, String accountType, int displayColor) { + return new StringBuilder(createKey(accountName, accountType)) + .append(SEPARATOR) + .append(displayColor) + .toString(); + } +} diff --git a/src/com/android/calendar/event/EventColorPickerDialog.java b/src/com/android/calendar/event/EventColorPickerDialog.java new file mode 100644 index 00000000..23c2d78e --- /dev/null +++ b/src/com/android/calendar/event/EventColorPickerDialog.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 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.event; + +import android.app.Activity; +import android.content.DialogInterface; +import android.os.Bundle; + +import com.android.calendar.R; +import com.android.calendar.color.ColorPickerDialog; + +/** + * A dialog which displays event colors, with an additional button for the calendar color. + */ +public class EventColorPickerDialog extends ColorPickerDialog { + + private static final int NUM_COLUMNS = 4; + + private int mCalendarColor; + + public EventColorPickerDialog() { + // Empty constructor required for dialog fragment. + } + + public EventColorPickerDialog(int[] colors, int selectedColor, int calendarColor, + boolean isTablet) { + super(R.string.event_color_picker_dialog_title, colors, selectedColor, NUM_COLUMNS, + isTablet ? SIZE_LARGE : SIZE_SMALL); + mCalendarColor = calendarColor; + } + + public void setCalendarColor(int color) { + mCalendarColor = color; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + final Activity activity = getActivity(); + mAlertDialog.setButton(DialogInterface.BUTTON_NEUTRAL, + activity.getString(R.string.event_color_set_to_default), + new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + if (mListener != null) { + mListener.onColorSelected(mCalendarColor); + } + } + } + ); + } +} |