From 0a600b84418e08c66b015cd57a29a40b2fe49648 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Wed, 2 Jun 2021 20:56:32 +0000 Subject: Converting mk to bp In order to remain consistent with Soong, we have converted Android.mk to Android.bp and deleted the old Android.mk file. Test: manual Change-Id: Iabae63198281d29d3502d0b34ce8b481cee534f6 --- Android.bp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Android.mk | 53 ----------------------------------------------------- 2 files changed, 54 insertions(+), 53 deletions(-) create mode 100644 Android.bp delete mode 100644 Android.mk diff --git a/Android.bp b/Android.bp new file mode 100644 index 00000000..8d6e06ad --- /dev/null +++ b/Android.bp @@ -0,0 +1,54 @@ +package { + + default_applicable_licenses: ["packages_apps_Calendar_license"], +} + +license { + + name: "packages_apps_Calendar_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +// Include res dir from chips +android_app { + name: "Calendar", + + jacoco: { + include_filter: ["com.android.calendar.**"], + }, + + srcs: [ + "src/**/*.java" + ], + + // bundled + //LOCAL_STATIC_JAVA_LIBRARIES += + //# android-common + //# libchips + //# calendar-common + + // unbundled + static_libs: [ + "android-common", + "libchips", + "colorpicker", + "android-opt-timezonepicker", + "androidx.legacy_legacy-support-v4", + "calendar-common", + ], + + sdk_version: "current", + optimize: { + proguard_flags_files: ["proguard.flags"], + }, + + product_specific: true, + + aaptflags: ["--auto-add-overlay"] +} diff --git a/Android.mk b/Android.mk deleted file mode 100644 index dce26a46..00000000 --- a/Android.mk +++ /dev/null @@ -1,53 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# Include res dir from chips -chips_dir := ../../../frameworks/opt/chips/res -color_picker_dir := ../../../frameworks/opt/colorpicker/res -timezonepicker_dir := ../../../frameworks/opt/timezonepicker/res -res_dirs := $(chips_dir) $(color_picker_dir) $(timezonepicker_dir) res -src_dirs := src - -LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.calendar.* - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under,$(src_dirs)) - -# bundled -#LOCAL_STATIC_JAVA_LIBRARIES += \ -# android-common \ -# libchips \ -# calendar-common - -# unbundled -LOCAL_STATIC_JAVA_LIBRARIES := \ - android-common \ - libchips \ - colorpicker \ - android-opt-timezonepicker \ - androidx.legacy_legacy-support-v4 \ - calendar-common - -LOCAL_SDK_VERSION := current - -LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs)) - -LOCAL_PACKAGE_NAME := Calendar -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE - -LOCAL_PROGUARD_FLAG_FILES := proguard.flags - -LOCAL_PRODUCT_MODULE := true - -LOCAL_AAPT_FLAGS := --auto-add-overlay -LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.chips -LOCAL_AAPT_FLAGS += --extra-packages com.android.colorpicker -LOCAL_AAPT_FLAGS += --extra-packages com.android.timezonepicker - -include $(BUILD_PACKAGE) - -# Use the following include to make our test apk. -include $(call all-makefiles-under,$(LOCAL_PATH)) -- cgit v1.2.3 From 24144814339e6cdf4361a23b93fd4ea726553a87 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 4 Jun 2021 18:52:01 +0000 Subject: AOSP / Calendar Android.bp changes We have tweaked the Android.bp file such that Mahi and I can add to our own exclude_srcs property. Moreover, the app name for the Kotlin target has been renamed to "Calendar" and that Java target has been renamed to "CalendarJava". Test: manual - builds successfully Change-Id: Id1ff4bdda9f39a1d94a67fde7249d1ccdb188a21 --- Android.bp | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/Android.bp b/Android.bp index 8d6e06ad..40983287 100644 --- a/Android.bp +++ b/Android.bp @@ -16,6 +16,10 @@ license { } // Include res dir from chips +exclude_srcsd = [] + +exclude_srcsm = [] + android_app { name: "Calendar", @@ -24,7 +28,47 @@ android_app { }, srcs: [ - "src/**/*.java" + "src/**/*.java", + "src/**/*.kt", + ], + + exclude_srcs: exclude_srcsd + exclude_srcsm, + + // bundled + //LOCAL_STATIC_JAVA_LIBRARIES += + //# android-common + //# libchips + //# calendar-common + + // unbundled + static_libs: [ + "android-common", + "libchips", + "colorpicker", + "android-opt-timezonepicker", + "androidx.legacy_legacy-support-v4", + "calendar-common", + ], + + sdk_version: "current", + optimize: { + proguard_flags_files: ["proguard.flags"], + }, + + product_specific: true, + + aaptflags: ["--auto-add-overlay"], +} + +android_app { + name: "CalendarJava", + + jacoco: { + include_filter: ["com.android.calendar.**"], + }, + + srcs: [ + "src/**/*.java", ], // bundled @@ -50,5 +94,5 @@ android_app { product_specific: true, - aaptflags: ["--auto-add-overlay"] + aaptflags: ["--auto-add-overlay"], } -- cgit v1.2.3 From c13dc3619e810b46e3c5a03e01de3a4b2fa5dd00 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Fri, 4 Jun 2021 23:54:39 +0000 Subject: AOSP/Calendar - Add Kotlin copy of GoogleCalendarUriIntentFilter.java Test: none, no functional change in this commit when merged with the corresponding Kotlin conversion change. Change-Id: I32f201a959866284780aba75c3fd7cd85dd0418e --- .../calendar/GoogleCalendarUriIntentFilter.kt | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/com/android/calendar/GoogleCalendarUriIntentFilter.kt diff --git a/src/com/android/calendar/GoogleCalendarUriIntentFilter.kt b/src/com/android/calendar/GoogleCalendarUriIntentFilter.kt new file mode 100644 index 00000000..45f92cf2 --- /dev/null +++ b/src/com/android/calendar/GoogleCalendarUriIntentFilter.kt @@ -0,0 +1,40 @@ +/* +** 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 android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.os.Bundle; + +public class GoogleCalendarUriIntentFilter extends Activity { + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + Intent intent = getIntent(); + if (intent != null) { + // Pass it on to the next Activity. + try { + startNextMatchingActivity(intent); + } catch (ActivityNotFoundException ex) { + // no browser installed? Just drop it. + } + } + finish(); + } +} -- cgit v1.2.3 From f794fea1a9a497a723f5fcf281f2db0349349492 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Sat, 5 Jun 2021 01:26:35 +0000 Subject: AOSP/Calendar - Adding corresponding Android.bp file with bpfmt -w check. Test: none, no functional change. Change-Id: Ib5c7d00d5ae96eb478f7c4627fd5bb0cda1c3e6c --- Android.bp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Android.bp b/Android.bp index 40983287..b1085f3c 100644 --- a/Android.bp +++ b/Android.bp @@ -18,7 +18,9 @@ license { // Include res dir from chips exclude_srcsd = [] -exclude_srcsm = [] +exclude_srcsm = [ + "src/**/calendar/GoogleCalendarUriIntentFilter.java", +] android_app { name: "Calendar", -- cgit v1.2.3 From 8f1879cce6ceed736f5582c495a32998005fedca Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Sat, 5 Jun 2021 00:38:47 +0000 Subject: AOSP/Calendar - Add Kotlin code for GoogleCalendarUriIntentFilter Test: ran CTS tests $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk BUG: 189873985 Change-Id: Ic09676530f7c175855ddffbeb11f3d35ec5cf625 --- .../calendar/GoogleCalendarUriIntentFilter.kt | 31 ++++++++++------------ 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/com/android/calendar/GoogleCalendarUriIntentFilter.kt b/src/com/android/calendar/GoogleCalendarUriIntentFilter.kt index 45f92cf2..d2fe77f9 100644 --- a/src/com/android/calendar/GoogleCalendarUriIntentFilter.kt +++ b/src/com/android/calendar/GoogleCalendarUriIntentFilter.kt @@ -1,5 +1,5 @@ /* -** Copyright 2009, The Android Open Source Project +** Copyright 2021, 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. @@ -13,28 +13,25 @@ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** limitations under the License. */ +package com.android.calendar -package com.android.calendar; +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.os.Bundle; - -public class GoogleCalendarUriIntentFilter extends Activity { - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - Intent intent = getIntent(); +class GoogleCalendarUriIntentFilter : Activity() { + protected override fun onCreate(icicle: Bundle?) { + super.onCreate(icicle) + val intent: Intent = getIntent() if (intent != null) { // Pass it on to the next Activity. try { - startNextMatchingActivity(intent); - } catch (ActivityNotFoundException ex) { + startNextMatchingActivity(intent) + } catch (ex: ActivityNotFoundException) { // no browser installed? Just drop it. } } - finish(); + finish() } -} +} \ No newline at end of file -- cgit v1.2.3 From 13b2e0af5fd7689b476d1c2eb06d32670814052e Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Tue, 8 Jun 2021 21:47:44 +0000 Subject: AOSP/Calendar - Add Kotlin copy of UpgradeReceiver.java Test: none, no functional change in this commit when merged with the corresponding Kotlin conversion change. Change-Id: I07fbf88d6be5a70d2d1741ebac84b8a6591b823c --- src/com/android/calendar/UpgradeReceiver.kt | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/com/android/calendar/UpgradeReceiver.kt diff --git a/src/com/android/calendar/UpgradeReceiver.kt b/src/com/android/calendar/UpgradeReceiver.kt new file mode 100644 index 00000000..0e89286d --- /dev/null +++ b/src/com/android/calendar/UpgradeReceiver.kt @@ -0,0 +1,29 @@ +/* + * 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; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class UpgradeReceiver extends BroadcastReceiver { + @Override + public void onReceive(final Context context, final Intent intent) { + Utils.trySyncAndDisableUpgradeReceiver(context); + } + +} \ No newline at end of file -- cgit v1.2.3 From c73e51ead8efb79e53f19f8150242e12043044c4 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Tue, 8 Jun 2021 22:14:27 +0000 Subject: AOSP/Calendar - Add Kotlin code for UpgradeReceiver.kt Also uploaded corresponding Android.bp file Test: manual - ran build and unit tests $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.165 OK (22 tests) BUG: 189873985 Change-Id: Ib8e854d1aac1b0cc7d853d3467db9431f635d66b --- Android.bp | 1 + src/com/android/calendar/UpgradeReceiver.kt | 19 ++++++++----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Android.bp b/Android.bp index b1085f3c..9e050cc8 100644 --- a/Android.bp +++ b/Android.bp @@ -20,6 +20,7 @@ exclude_srcsd = [] exclude_srcsm = [ "src/**/calendar/GoogleCalendarUriIntentFilter.java", + "src/**/calendar/UpgradeReceiver.java", ] android_app { diff --git a/src/com/android/calendar/UpgradeReceiver.kt b/src/com/android/calendar/UpgradeReceiver.kt index 0e89286d..ab2de1de 100644 --- a/src/com/android/calendar/UpgradeReceiver.kt +++ b/src/com/android/calendar/UpgradeReceiver.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,17 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar -package com.android.calendar; +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class UpgradeReceiver extends BroadcastReceiver { - @Override - public void onReceive(final Context context, final Intent intent) { - Utils.trySyncAndDisableUpgradeReceiver(context); +class UpgradeReceiver : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + Utils.trySyncAndDisableUpgradeReceiver(context) } - } \ No newline at end of file -- cgit v1.2.3 From 48bf7d84ec9d7405f6e063995540ef6dd14a72df Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 4 Jun 2021 22:38:33 +0000 Subject: AOSP/Calendar - Copy of AlertUtils.java in AlertUtils.kt The Java code in AlertUtils.java has been copied into a new file named AlertUtils.kt. Test: manual - opening both files clearly shows they are identical. Change-Id: I58a1179de7350e84cd02ee419038692287b14cfe --- src/com/android/calendar/alerts/AlertUtils.kt | 108 ++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/com/android/calendar/alerts/AlertUtils.kt diff --git a/src/com/android/calendar/alerts/AlertUtils.kt b/src/com/android/calendar/alerts/AlertUtils.kt new file mode 100644 index 00000000..b9aaec29 --- /dev/null +++ b/src/com/android/calendar/alerts/AlertUtils.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2012 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.alerts; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.provider.CalendarContract; +import android.provider.CalendarContract.CalendarAlerts; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Log; + +import com.android.calendar.EventInfoActivity; +import com.android.calendar.R; +import com.android.calendar.Utils; + +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + +public class AlertUtils { + private static final String TAG = "AlertUtils"; + static final boolean DEBUG = true; + + public static final long SNOOZE_DELAY = 5 * 60 * 1000L; + + // We use one notification id for the expired events notification. All + // other notifications (the 'active' future/concurrent ones) use a unique ID. + public static final int EXPIRED_GROUP_NOTIFICATION_ID = 0; + + public static final String EVENT_ID_KEY = "eventid"; + public static final String EVENT_START_KEY = "eventstart"; + public static final String EVENT_END_KEY = "eventend"; + public static final String NOTIFICATION_ID_KEY = "notificationid"; + public static final String EVENT_IDS_KEY = "eventids"; + public static final String EVENT_STARTS_KEY = "starts"; + + // A flag for using local storage to save alert state instead of the alerts DB table. + // This allows the unbundled app to run alongside other calendar apps without eating + // alerts from other apps. + static boolean BYPASS_DB = true; + + /** + * Creates an AlarmManagerInterface that wraps a real AlarmManager. The alarm code + * was abstracted to an interface to make it testable. + */ + public static AlarmManagerInterface createAlarmManager(Context context) { + final AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + return new AlarmManagerInterface() { + @Override + public void set(int type, long triggerAtMillis, PendingIntent operation) { + if (Utils.isKeyLimePieOrLater()) { + mgr.setExact(type, triggerAtMillis, operation); + } else { + mgr.set(type, triggerAtMillis, operation); + } + } + }; + } + + /** + * Schedules an alarm intent with the system AlarmManager that will notify + * listeners when a reminder should be fired. The provider will keep + * scheduled reminders up to date but apps may use this to implement snooze + * functionality without modifying the reminders table. Scheduled alarms + * will generate an intent using AlertReceiver.EVENT_REMINDER_APP_ACTION. + * + * @param context A context for referencing system resources + * @param manager The AlarmManager to use or null + * @param alarmTime The time to fire the intent in UTC millis since epoch + */ + public static void scheduleAlarm(Context context, AlarmManagerInterface manager, + long alarmTime) { + } + + public static Intent buildEventViewIntent(Context c, long eventId, long begin, long end) { + Intent i = new Intent(Intent.ACTION_VIEW); + Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon(); + builder.appendEncodedPath("events/" + eventId); + i.setData(builder.build()); + i.setClass(c, EventInfoActivity.class); + i.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, begin); + i.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end); + return i; + } +} -- cgit v1.2.3 From c57766ab83f187738c033087b9a8d25ab4b63b74 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Tue, 8 Jun 2021 20:54:15 +0000 Subject: AOSP/Calendar - Adding corresponding Android.bp file Test: manual - there is no functional change. We ran bpfmt -w in order to correctly format the file according to stylistic guides. Change-Id: I59264bdec303e1749fce0e6016fd13b6045be5e5 --- Android.bp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.bp b/Android.bp index 9e050cc8..22a2458b 100644 --- a/Android.bp +++ b/Android.bp @@ -16,7 +16,7 @@ license { } // Include res dir from chips -exclude_srcsd = [] +exclude_srcsd = ["src/**/calendar/alerts/AlertUtils.java"] exclude_srcsm = [ "src/**/calendar/GoogleCalendarUriIntentFilter.java", -- cgit v1.2.3 From 1c4de12d18bddeabd8c2c8588ce307520983029c Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Tue, 8 Jun 2021 20:59:37 +0000 Subject: AOSP/Calendar - AlertUtils.java fully converted to Kotlin Using Android Studio, AlertUtils.java has been converted to Kotlin. Test: manual - build tests and unit tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am intrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.157 OK (22 tests) Change-Id: I9ac7af29bc1179191c7985ecfe8b513b359a0a0f --- src/com/android/calendar/alerts/AlertUtils.kt | 104 +++++++++++--------------- 1 file changed, 44 insertions(+), 60 deletions(-) diff --git a/src/com/android/calendar/alerts/AlertUtils.kt b/src/com/android/calendar/alerts/AlertUtils.kt index b9aaec29..18b7e7d1 100644 --- a/src/com/android/calendar/alerts/AlertUtils.kt +++ b/src/com/android/calendar/alerts/AlertUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,71 +13,52 @@ * See the License for the specific language governing permissions and * limitations under the License */ +package com.android.calendar.alerts -package com.android.calendar.alerts; +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.provider.CalendarContract +import com.android.calendar.EventInfoActivity +import com.android.calendar.Utils -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.Uri; -import android.provider.CalendarContract; -import android.provider.CalendarContract.CalendarAlerts; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; - -import com.android.calendar.EventInfoActivity; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; - -public class AlertUtils { - private static final String TAG = "AlertUtils"; - static final boolean DEBUG = true; - - public static final long SNOOZE_DELAY = 5 * 60 * 1000L; +object AlertUtils { + private const val TAG = "AlertUtils" + const val DEBUG = true + const val SNOOZE_DELAY = 5 * 60 * 1000L // We use one notification id for the expired events notification. All // other notifications (the 'active' future/concurrent ones) use a unique ID. - public static final int EXPIRED_GROUP_NOTIFICATION_ID = 0; - - public static final String EVENT_ID_KEY = "eventid"; - public static final String EVENT_START_KEY = "eventstart"; - public static final String EVENT_END_KEY = "eventend"; - public static final String NOTIFICATION_ID_KEY = "notificationid"; - public static final String EVENT_IDS_KEY = "eventids"; - public static final String EVENT_STARTS_KEY = "starts"; + const val EXPIRED_GROUP_NOTIFICATION_ID = 0 + const val EVENT_ID_KEY = "eventid" + const val EVENT_START_KEY = "eventstart" + const val EVENT_END_KEY = "eventend" + const val NOTIFICATION_ID_KEY = "notificationid" + const val EVENT_IDS_KEY = "eventids" + const val EVENT_STARTS_KEY = "starts" // A flag for using local storage to save alert state instead of the alerts DB table. // This allows the unbundled app to run alongside other calendar apps without eating // alerts from other apps. - static boolean BYPASS_DB = true; + var BYPASS_DB = true /** * Creates an AlarmManagerInterface that wraps a real AlarmManager. The alarm code * was abstracted to an interface to make it testable. */ - public static AlarmManagerInterface createAlarmManager(Context context) { - final AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - return new AlarmManagerInterface() { - @Override - public void set(int type, long triggerAtMillis, PendingIntent operation) { - if (Utils.isKeyLimePieOrLater()) { - mgr.setExact(type, triggerAtMillis, operation); + @JvmStatic fun createAlarmManager(context: Context): AlarmManagerInterface { + val mgr: AlarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + return object : AlarmManagerInterface { + override operator fun set(type: Int, triggerAtMillis: Long, operation: PendingIntent?) { + if (com.android.calendar.Utils.isKeyLimePieOrLater()) { + mgr.setExact(type, triggerAtMillis, operation) } else { - mgr.set(type, triggerAtMillis, operation); + mgr.set(type, triggerAtMillis, operation) } } - }; + } } /** @@ -91,18 +72,21 @@ public class AlertUtils { * @param manager The AlarmManager to use or null * @param alarmTime The time to fire the intent in UTC millis since epoch */ - public static void scheduleAlarm(Context context, AlarmManagerInterface manager, - long alarmTime) { + @JvmStatic fun scheduleAlarm( + context: Context?, + manager: AlarmManagerInterface?, + alarmTime: Long + ) { } - public static Intent buildEventViewIntent(Context c, long eventId, long begin, long end) { - Intent i = new Intent(Intent.ACTION_VIEW); - Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon(); - builder.appendEncodedPath("events/" + eventId); - i.setData(builder.build()); - i.setClass(c, EventInfoActivity.class); - i.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, begin); - i.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end); - return i; + @JvmStatic fun buildEventViewIntent(c: Context, eventId: Long, begin: Long, end: Long): Intent { + val i = Intent(Intent.ACTION_VIEW) + val builder: Uri.Builder = CalendarContract.CONTENT_URI.buildUpon() + builder.appendEncodedPath("events/$eventId") + i.setData(builder.build()) + i.setClass(c, EventInfoActivity::class.java) + i.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, begin) + i.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end) + return i } } -- cgit v1.2.3 From 862996214629065102ab8160c08c0aa0f77ab790 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Wed, 9 Jun 2021 18:37:39 +0000 Subject: AOSP/Calendar - Add Kotlin copy of following files: AsyncQueryServiceHelper.java, CalendarApplication.java, CalendarBackupAgent.java Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: I8a1a0aaa7f7b222342ea957951caff0094f10370 --- .../android/calendar/AsyncQueryServiceHelper.kt | 70 ++++++++++++++++++++++ src/com/android/calendar/CalendarApplication.kt | 32 ++++++++++ src/com/android/calendar/CalendarBackupAgent.kt | 41 +++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 src/com/android/calendar/AsyncQueryServiceHelper.kt create mode 100644 src/com/android/calendar/CalendarApplication.kt create mode 100644 src/com/android/calendar/CalendarBackupAgent.kt diff --git a/src/com/android/calendar/AsyncQueryServiceHelper.kt b/src/com/android/calendar/AsyncQueryServiceHelper.kt new file mode 100644 index 00000000..c6e0a2bc --- /dev/null +++ b/src/com/android/calendar/AsyncQueryServiceHelper.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 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.app.IntentService; +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.net.Uri; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.PriorityQueue; +import java.util.concurrent.Delayed; +import java.util.concurrent.TimeUnit; + +public class AsyncQueryServiceHelper extends IntentService { + private static final String TAG = "AsyncQuery"; + + public AsyncQueryServiceHelper(String name) { + super(name); + } + + public AsyncQueryServiceHelper() { + super("AsyncQueryServiceHelper"); + } + + @Override + protected void onHandleIntent(Intent intent) { + } + + @Override + public void onStart(Intent intent, int startId) { + super.onStart(intent, startId); + } + + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } +} diff --git a/src/com/android/calendar/CalendarApplication.kt b/src/com/android/calendar/CalendarApplication.kt new file mode 100644 index 00000000..d0ca4698 --- /dev/null +++ b/src/com/android/calendar/CalendarApplication.kt @@ -0,0 +1,32 @@ +/* + * 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.app.Application; + +public class CalendarApplication extends Application { + @Override + public void onCreate() { + super.onCreate(); + + /* + * Ensure the default values are set for any receiver, activity, + * service, etc. of Calendar + */ + GeneralPreferences.setDefaultValues(this); + } +} diff --git a/src/com/android/calendar/CalendarBackupAgent.kt b/src/com/android/calendar/CalendarBackupAgent.kt new file mode 100644 index 00000000..02456fdc --- /dev/null +++ b/src/com/android/calendar/CalendarBackupAgent.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 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.app.backup.BackupAgentHelper; +import android.app.backup.BackupDataInput; +import android.app.backup.SharedPreferencesBackupHelper; +import android.content.Context; +import android.content.SharedPreferences.Editor; +import android.os.ParcelFileDescriptor; + +import java.io.IOException; + +public class CalendarBackupAgent extends BackupAgentHelper +{ + static final String SHARED_KEY = "shared_pref"; + + @Override + public void onCreate() { + } + + @Override + public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) + throws IOException { + super.onRestore(data, appVersionCode, newState); + } +} -- cgit v1.2.3 From 46a22909ac41dd70960bf08cafc4ec2529dcb781 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Wed, 9 Jun 2021 19:36:02 +0000 Subject: AOSP/Calendar - Add Kotlin code for AsyncQueryServiceHelper.kt, CalendarApplication.kt, CalendarBackupAgent.kt. Also uploaded corresponding Android.bp file Test: manual (ran build and unit tests) and CTS testing suite $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.138 OK (22 tests) CTS TESTING: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 11m 36s Total aggregated tests run time: 11m 36s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.54 tests/sec [376 tests / 696574 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 11118 ms || clean = 3133 ms Total preparation time: 11s || Total tear down time: 3s ======================================================= =============== Summary =============== Total Run time: 17m 42s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ BUG: 189873985 Change-Id: Id0c28288c0a03feebf8336eeec1f14f1e86990e7 --- Android.bp | 3 + .../android/calendar/AsyncQueryServiceHelper.kt | 85 ++++++++++------------ src/com/android/calendar/CalendarApplication.kt | 18 ++--- src/com/android/calendar/CalendarBackupAgent.kt | 39 +++++----- 4 files changed, 68 insertions(+), 77 deletions(-) diff --git a/Android.bp b/Android.bp index 22a2458b..29f86ed0 100644 --- a/Android.bp +++ b/Android.bp @@ -19,6 +19,9 @@ license { exclude_srcsd = ["src/**/calendar/alerts/AlertUtils.java"] exclude_srcsm = [ + "src/**/calendar/AsyncQueryServiceHelper.java", + "src/**/calendar/CalendarApplication.java", + "src/**/calendar/CalendarBackupAgent.java", "src/**/calendar/GoogleCalendarUriIntentFilter.java", "src/**/calendar/UpgradeReceiver.java", ] diff --git a/src/com/android/calendar/AsyncQueryServiceHelper.kt b/src/com/android/calendar/AsyncQueryServiceHelper.kt index c6e0a2bc..47973304 100644 --- a/src/com/android/calendar/AsyncQueryServiceHelper.kt +++ b/src/com/android/calendar/AsyncQueryServiceHelper.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,58 +13,49 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar; - -import android.app.IntentService; -import android.content.ContentProviderOperation; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.OperationApplicationException; -import android.database.Cursor; -import android.net.Uri; -import android.os.Handler; -import android.os.Message; -import android.os.RemoteException; -import android.os.SystemClock; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.PriorityQueue; -import java.util.concurrent.Delayed; -import java.util.concurrent.TimeUnit; - -public class AsyncQueryServiceHelper extends IntentService { - private static final String TAG = "AsyncQuery"; - - public AsyncQueryServiceHelper(String name) { - super(name); - } - - public AsyncQueryServiceHelper() { - super("AsyncQueryServiceHelper"); +package com.android.calendar + +import android.app.IntentService +import android.content.ContentProviderOperation +import android.content.ContentResolver +import android.content.ContentValues +import android.content.Context +import android.content.Intent +import android.content.OperationApplicationException +import android.database.Cursor +import android.net.Uri +import android.os.Handler +import android.os.Message +import android.os.RemoteException +import android.os.SystemClock +import android.util.Log +import java.util.ArrayList +import java.util.Arrays +import java.util.Iterator +import java.util.PriorityQueue +import java.util.concurrent.Delayed +import java.util.concurrent.TimeUnit + +class AsyncQueryServiceHelper : IntentService { + constructor(name: String?) : super(name) {} + constructor() : super("AsyncQueryServiceHelper") {} + + protected override fun onHandleIntent(intent: Intent?) { } - @Override - protected void onHandleIntent(Intent intent) { + override fun onStart(intent: Intent?, startId: Int) { + super.onStart(intent, startId) } - @Override - public void onStart(Intent intent, int startId) { - super.onStart(intent, startId); + override fun onCreate() { + super.onCreate() } - @Override - public void onCreate() { - super.onCreate(); + override fun onDestroy() { + super.onDestroy() } - @Override - public void onDestroy() { - super.onDestroy(); + companion object { + private const val TAG = "AsyncQuery" } -} +} \ No newline at end of file diff --git a/src/com/android/calendar/CalendarApplication.kt b/src/com/android/calendar/CalendarApplication.kt index d0ca4698..445d7257 100644 --- a/src/com/android/calendar/CalendarApplication.kt +++ b/src/com/android/calendar/CalendarApplication.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,20 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar -package com.android.calendar; +import android.app.Application -import android.app.Application; - -public class CalendarApplication extends Application { - @Override - public void onCreate() { - super.onCreate(); +class CalendarApplication : Application() { + override fun onCreate() { + super.onCreate() /* * Ensure the default values are set for any receiver, activity, * service, etc. of Calendar */ - GeneralPreferences.setDefaultValues(this); + GeneralPreferences.setDefaultValues(this) } -} +} \ No newline at end of file diff --git a/src/com/android/calendar/CalendarBackupAgent.kt b/src/com/android/calendar/CalendarBackupAgent.kt index 02456fdc..f3e230ac 100644 --- a/src/com/android/calendar/CalendarBackupAgent.kt +++ b/src/com/android/calendar/CalendarBackupAgent.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,29 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar -package com.android.calendar; +import android.app.backup.BackupAgentHelper +import android.app.backup.BackupDataInput +import android.app.backup.SharedPreferencesBackupHelper +import android.content.Context +import android.content.SharedPreferences.Editor +import android.os.ParcelFileDescriptor -import android.app.backup.BackupAgentHelper; -import android.app.backup.BackupDataInput; -import android.app.backup.SharedPreferencesBackupHelper; -import android.content.Context; -import android.content.SharedPreferences.Editor; -import android.os.ParcelFileDescriptor; +import java.io.IOException -import java.io.IOException; - -public class CalendarBackupAgent extends BackupAgentHelper -{ - static final String SHARED_KEY = "shared_pref"; +class CalendarBackupAgent : BackupAgentHelper() { + override fun onCreate() { + } - @Override - public void onCreate() { + @Throws(IOException::class) + override fun onRestore(data: BackupDataInput?, appVersionCode: Int, + newState: ParcelFileDescriptor?) { + super.onRestore(data, appVersionCode, newState) } - @Override - public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) - throws IOException { - super.onRestore(data, appVersionCode, newState); + companion object { + const val SHARED_KEY = "shared_pref" } -} +} \ No newline at end of file -- cgit v1.2.3 From 8754af4b42b0009eba71c73f7dbd8f2daace9c2e Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 10 Jun 2021 20:49:57 +0000 Subject: AOSP/Calendar - Copy of DayOfMonthDrawable.java in DayOfMonthDrawable.kt The Java code in DayOfMonthDrawable.java has been copied into a new file named DayOfMonthDrawable.kt. Test: manual - opening both files clearly shows they are identical. Change-Id: I55c0f3f33863bb9e23faa7d3fc2034daea44ab28 --- src/com/android/calendar/DayOfMonthDrawable.kt | 77 ++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/com/android/calendar/DayOfMonthDrawable.kt diff --git a/src/com/android/calendar/DayOfMonthDrawable.kt b/src/com/android/calendar/DayOfMonthDrawable.kt new file mode 100644 index 00000000..461ab317 --- /dev/null +++ b/src/com/android/calendar/DayOfMonthDrawable.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 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.Context; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; + +/** + * A custom view to draw the day of the month in the today button in the options menu + */ + +public class DayOfMonthDrawable extends Drawable { + + private String mDayOfMonth = "1"; + private final Paint mPaint; + private final Rect mTextBounds = new Rect(); + private static float mTextSize = 14; + + public DayOfMonthDrawable(Context c) { + mTextSize = c.getResources().getDimension(R.dimen.today_icon_text_size); + mPaint = new Paint(); + mPaint.setAlpha(255); + mPaint.setColor(0xFF777777); + mPaint.setTypeface(Typeface.DEFAULT_BOLD); + mPaint.setTextSize(mTextSize); + mPaint.setTextAlign(Paint.Align.CENTER); + } + + @Override + public void draw(Canvas canvas) { + mPaint.getTextBounds(mDayOfMonth, 0, mDayOfMonth.length(), mTextBounds); + int textHeight = mTextBounds.bottom - mTextBounds.top; + Rect bounds = getBounds(); + canvas.drawText(mDayOfMonth, bounds.right / 2, ((float) bounds.bottom + textHeight + 1) / 2, + mPaint); + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + // Ignore + } + + @Override + public int getOpacity() { + return PixelFormat.UNKNOWN; + } + + public void setDayOfMonth(int day) { + mDayOfMonth = Integer.toString(day); + invalidateSelf(); + } +} -- cgit v1.2.3 From 2c93b9549f28175425bc2b889306e1012d662a11 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 10 Jun 2021 21:11:09 +0000 Subject: AOSP/Calendar - DayOfMonthDrawable.java fully converted with bp file Using Android Studio, DayOfMonthDrawable.java has been converted to Kotlin. The Android.bp file has also been altered to accommodate the new change. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.161 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ============================================ ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 8m 27s Total aggregated tests run time: 8m 27s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.74 tests/sec [376 tests / 507657 msec] Change-Id: I5d0c9fa05f9bb05a3715e11a2df91e8f2d2eb2fb --- Android.bp | 5 +- src/com/android/calendar/DayOfMonthDrawable.kt | 89 +++++++++++++------------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/Android.bp b/Android.bp index 29f86ed0..0e825764 100644 --- a/Android.bp +++ b/Android.bp @@ -16,7 +16,10 @@ license { } // Include res dir from chips -exclude_srcsd = ["src/**/calendar/alerts/AlertUtils.java"] +exclude_srcsd = [ + "src/**/calendar/alerts/AlertUtils.java", + "src/**/calendar/DayOfMonthDrawable.java", +] exclude_srcsm = [ "src/**/calendar/AsyncQueryServiceHelper.java", diff --git a/src/com/android/calendar/DayOfMonthDrawable.kt b/src/com/android/calendar/DayOfMonthDrawable.kt index 461ab317..e348b5a2 100644 --- a/src/com/android/calendar/DayOfMonthDrawable.kt +++ b/src/com/android/calendar/DayOfMonthDrawable.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,65 +13,62 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar -package com.android.calendar; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; +import android.content.Context +import android.graphics.Canvas +import android.graphics.ColorFilter +import android.graphics.Paint +import android.graphics.PixelFormat +import android.graphics.Rect +import android.graphics.Typeface +import android.graphics.drawable.Drawable /** * A custom view to draw the day of the month in the today button in the options menu */ +class DayOfMonthDrawable(c: Context) : Drawable() { + private var mDayOfMonth = "1" + private val mPaint: Paint + private val mTextBounds: Rect = Rect() + override fun draw(canvas: Canvas) { + mPaint.getTextBounds(mDayOfMonth, 0, mDayOfMonth.length, mTextBounds) + val textHeight: Int = mTextBounds.bottom - mTextBounds.top + val bounds: Rect = getBounds() + canvas.drawText( + mDayOfMonth, (bounds.right).toFloat() / 2f, ((bounds.bottom).toFloat() + + textHeight + 1) / 2f, mPaint + ) + } -public class DayOfMonthDrawable extends Drawable { - - private String mDayOfMonth = "1"; - private final Paint mPaint; - private final Rect mTextBounds = new Rect(); - private static float mTextSize = 14; - - public DayOfMonthDrawable(Context c) { - mTextSize = c.getResources().getDimension(R.dimen.today_icon_text_size); - mPaint = new Paint(); - mPaint.setAlpha(255); - mPaint.setColor(0xFF777777); - mPaint.setTypeface(Typeface.DEFAULT_BOLD); - mPaint.setTextSize(mTextSize); - mPaint.setTextAlign(Paint.Align.CENTER); + override fun setAlpha(alpha: Int) { + mPaint.setAlpha(alpha) } - @Override - public void draw(Canvas canvas) { - mPaint.getTextBounds(mDayOfMonth, 0, mDayOfMonth.length(), mTextBounds); - int textHeight = mTextBounds.bottom - mTextBounds.top; - Rect bounds = getBounds(); - canvas.drawText(mDayOfMonth, bounds.right / 2, ((float) bounds.bottom + textHeight + 1) / 2, - mPaint); + override fun setColorFilter(cf: ColorFilter?) { + // Ignore } - @Override - public void setAlpha(int alpha) { - mPaint.setAlpha(alpha); + override fun getOpacity(): Int { + return PixelFormat.UNKNOWN } - @Override - public void setColorFilter(ColorFilter cf) { - // Ignore + fun setDayOfMonth(day: Int) { + mDayOfMonth = Integer.toString(day) + invalidateSelf() } - @Override - public int getOpacity() { - return PixelFormat.UNKNOWN; + companion object { + private var mTextSize = 14f } - public void setDayOfMonth(int day) { - mDayOfMonth = Integer.toString(day); - invalidateSelf(); + init { + mTextSize = c.getResources().getDimension(R.dimen.today_icon_text_size) + mPaint = Paint() + mPaint.setAlpha(255) + mPaint.setColor(-0x888889) + mPaint.setTypeface(Typeface.DEFAULT_BOLD) + mPaint.setTextSize(mTextSize) + mPaint.setTextAlign(Paint.Align.CENTER) } -} +} \ No newline at end of file -- cgit v1.2.3 From 98fd08d2a4e4f9b14ae6ef8e2f454257391855e5 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 11 Jun 2021 14:56:14 +0000 Subject: AOSP/Calendar - Copy of AlertReceiver.java in .kt file The Java code in AlertReceiver.java has been copied into a new filed named AlertReceiver.kt. Test: manual - opening both files shows they are identical. Change-Id: I341a0e973d398040e18870d7195e8fa696bd12b1 --- src/com/android/calendar/alerts/AlertReceiver.kt | 123 +++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/com/android/calendar/alerts/AlertReceiver.kt diff --git a/src/com/android/calendar/alerts/AlertReceiver.kt b/src/com/android/calendar/alerts/AlertReceiver.kt new file mode 100644 index 00000000..ce80cae1 --- /dev/null +++ b/src/com/android/calendar/alerts/AlertReceiver.kt @@ -0,0 +1,123 @@ +/* + * 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.alerts; + +import android.app.Notification; +import android.app.PendingIntent; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.database.Cursor; +import android.net.Uri; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.PowerManager; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Events; +import android.telephony.TelephonyManager; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.text.style.RelativeSizeSpan; +import android.text.style.TextAppearanceSpan; +import android.text.style.URLSpan; +import android.util.Log; +import android.view.View; +import android.widget.RemoteViews; + +import com.android.calendar.R; +import com.android.calendar.Utils; +import com.android.calendar.alerts.AlertService.NotificationWrapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +/** + * Receives android.intent.action.EVENT_REMINDER intents and handles + * event reminders. The intent URI specifies an alert id in the + * CalendarAlerts database table. This class also receives the + * BOOT_COMPLETED intent so that it can add a status bar notification + * if there are Calendar event alarms that have not been dismissed. + * It also receives the TIME_CHANGED action so that it can fire off + * snoozed alarms that have become ready. The real work is done in + * the AlertService class. + * + * To trigger this code after pushing the apk to device: + * adb shell am broadcast -a "android.intent.action.EVENT_REMINDER" + * -n "com.android.calendar/.alerts.AlertReceiver" + */ +public class AlertReceiver extends BroadcastReceiver { + private static final String TAG = "AlertReceiver"; + + // The broadcast for notification refreshes scheduled by the app. This is to + // distinguish the EVENT_REMINDER broadcast sent by the provider. + public static final String EVENT_REMINDER_APP_ACTION = + "com.android.calendar.EVENT_REMINDER_APP"; + + public static final String ACTION_DISMISS_OLD_REMINDERS = "removeOldReminders"; + + @Override + public void onReceive(final Context context, final Intent intent) { + if (AlertService.DEBUG) { + Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); + } + closeNotificationShade(context); + } + + public static NotificationWrapper makeBasicNotification(Context context, String title, + String summaryText, long startMillis, long endMillis, long eventId, + int notificationId, boolean doPopup, int priority) { + Notification n = buildBasicNotification(new Notification.Builder(context), + context, title, summaryText, startMillis, endMillis, eventId, notificationId, + doPopup, priority, false); + return new NotificationWrapper(n, notificationId, eventId, startMillis, endMillis, doPopup); + } + + private static Notification buildBasicNotification(Notification.Builder notificationBuilder, + Context context, String title, String summaryText, long startMillis, long endMillis, + long eventId, int notificationId, boolean doPopup, int priority, + boolean addActionButtons) { + Resources resources = context.getResources(); + if (title == null || title.length() == 0) { + title = resources.getString(R.string.no_title_label); + } + + // Create the base notification. + notificationBuilder.setContentTitle(title); + notificationBuilder.setContentText(summaryText); + notificationBuilder.setSmallIcon(R.drawable.stat_notify_calendar); + if (Utils.isJellybeanOrLater()) { + // Turn off timestamp. + notificationBuilder.setWhen(0); + + // Should be one of the values in Notification (ie. Notification.PRIORITY_HIGH, etc). + // A higher priority will encourage notification manager to expand it. + notificationBuilder.setPriority(priority); + } + return notificationBuilder.getNotification(); + } + + private void closeNotificationShade(Context context) { + Intent closeNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + context.sendBroadcast(closeNotificationShadeIntent); + } +} -- cgit v1.2.3 From d40c32416017b346cdaca8d47d21cb8d881346cd Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 11 Jun 2021 16:15:31 +0000 Subject: AOSP/Calendar - AlertReceiver.java fully converted with bp file Using Android Studio, AlertReceiver.java has been converted to Kotlin. The Android.bp file has also been added to in order to accommodate for the new file. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.151 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 12m 53s Total aggregated tests run time: 12m 53s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.49 tests/sec [376 tests / 773497 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 7789 ms || clean = 2001 ms Total preparation time: 7s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 14m 24s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: I41d434835dd1ca12a67e366a477c1ac6568debae --- Android.bp | 1 + src/com/android/calendar/alerts/AlertReceiver.kt | 165 ++++++++++++----------- 2 files changed, 84 insertions(+), 82 deletions(-) diff --git a/Android.bp b/Android.bp index 0e825764..375564d6 100644 --- a/Android.bp +++ b/Android.bp @@ -17,6 +17,7 @@ license { // Include res dir from chips exclude_srcsd = [ + "src/**/calendar/alerts/AlertReceiver.java", "src/**/calendar/alerts/AlertUtils.java", "src/**/calendar/DayOfMonthDrawable.java", ] diff --git a/src/com/android/calendar/alerts/AlertReceiver.kt b/src/com/android/calendar/alerts/AlertReceiver.kt index ce80cae1..31750065 100644 --- a/src/com/android/calendar/alerts/AlertReceiver.kt +++ b/src/com/android/calendar/alerts/AlertReceiver.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,43 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar.alerts -package com.android.calendar.alerts; - -import android.app.Notification; -import android.app.PendingIntent; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.PowerManager; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.telephony.TelephonyManager; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.text.style.RelativeSizeSpan; -import android.text.style.TextAppearanceSpan; -import android.text.style.URLSpan; -import android.util.Log; -import android.view.View; -import android.widget.RemoteViews; - -import com.android.calendar.R; -import com.android.calendar.Utils; -import com.android.calendar.alerts.AlertService.NotificationWrapper; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; +import android.app.Notification +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.res.Resources +import android.util.Log +import com.android.calendar.R +import com.android.calendar.Utils +import com.android.calendar.alerts.AlertService.NotificationWrapper /** * Receives android.intent.action.EVENT_REMINDER intents and handles @@ -63,61 +37,88 @@ import java.util.regex.Pattern; * * To trigger this code after pushing the apk to device: * adb shell am broadcast -a "android.intent.action.EVENT_REMINDER" - * -n "com.android.calendar/.alerts.AlertReceiver" + * -n "com.android.calendar/.alerts.AlertReceiver" */ -public class AlertReceiver extends BroadcastReceiver { - private static final String TAG = "AlertReceiver"; - - // The broadcast for notification refreshes scheduled by the app. This is to - // distinguish the EVENT_REMINDER broadcast sent by the provider. - public static final String EVENT_REMINDER_APP_ACTION = - "com.android.calendar.EVENT_REMINDER_APP"; - - public static final String ACTION_DISMISS_OLD_REMINDERS = "removeOldReminders"; - +class AlertReceiver : BroadcastReceiver() { @Override - public void onReceive(final Context context, final Intent intent) { + override fun onReceive(context: Context, intent: Intent) { if (AlertService.DEBUG) { - Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); + Log.d(TAG, "onReceive: a=" + intent.getAction().toString() + " " + intent.toString()) } - closeNotificationShade(context); + closeNotificationShade(context) } - public static NotificationWrapper makeBasicNotification(Context context, String title, - String summaryText, long startMillis, long endMillis, long eventId, - int notificationId, boolean doPopup, int priority) { - Notification n = buildBasicNotification(new Notification.Builder(context), - context, title, summaryText, startMillis, endMillis, eventId, notificationId, - doPopup, priority, false); - return new NotificationWrapper(n, notificationId, eventId, startMillis, endMillis, doPopup); + private fun closeNotificationShade(context: Context) { + val closeNotificationShadeIntent = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) + context.sendBroadcast(closeNotificationShadeIntent) } - private static Notification buildBasicNotification(Notification.Builder notificationBuilder, - Context context, String title, String summaryText, long startMillis, long endMillis, - long eventId, int notificationId, boolean doPopup, int priority, - boolean addActionButtons) { - Resources resources = context.getResources(); - if (title == null || title.length() == 0) { - title = resources.getString(R.string.no_title_label); + companion object { + private const val TAG = "AlertReceiver" + + // The broadcast for notification refreshes scheduled by the app. This is to + // distinguish the EVENT_REMINDER broadcast sent by the provider. + const val EVENT_REMINDER_APP_ACTION = "com.android.calendar.EVENT_REMINDER_APP" + const val ACTION_DISMISS_OLD_REMINDERS = "removeOldReminders" + fun makeBasicNotification( + context: Context, + title: String, + summaryText: String, + startMillis: Long, + endMillis: Long, + eventId: Long, + notificationId: Int, + doPopup: Boolean, + priority: Int + ): NotificationWrapper { + val n: Notification = buildBasicNotification( + Notification.Builder(context), + context, + title, + summaryText, + startMillis, + endMillis, + eventId, + notificationId, + doPopup, + priority, false + ) + return NotificationWrapper(n, notificationId, eventId, startMillis, endMillis, doPopup) } - // Create the base notification. - notificationBuilder.setContentTitle(title); - notificationBuilder.setContentText(summaryText); - notificationBuilder.setSmallIcon(R.drawable.stat_notify_calendar); - if (Utils.isJellybeanOrLater()) { - // Turn off timestamp. - notificationBuilder.setWhen(0); + private fun buildBasicNotification( + notificationBuilder: Notification.Builder, + context: Context, + title: String, + summaryText: String, + startMillis: Long, + endMillis: Long, + eventId: Long, + notificationId: Int, + doPopup: Boolean, + priority: Int, + addActionButtons: Boolean + ): Notification { + var title: String? = title + val resources: Resources = context.getResources() + if (title == null || title.length == 0) { + title = resources.getString(R.string.no_title_label) + } - // Should be one of the values in Notification (ie. Notification.PRIORITY_HIGH, etc). - // A higher priority will encourage notification manager to expand it. - notificationBuilder.setPriority(priority); - } - return notificationBuilder.getNotification(); - } + // Create the base notification. + notificationBuilder.setContentTitle(title) + notificationBuilder.setContentText(summaryText) + notificationBuilder.setSmallIcon(R.drawable.stat_notify_calendar) + if (Utils.isJellybeanOrLater()) { + // Turn off timestamp. + notificationBuilder.setWhen(0) - private void closeNotificationShade(Context context) { - Intent closeNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - context.sendBroadcast(closeNotificationShadeIntent); + // Should be one of the values in Notification + // (ie. Notification.PRIORITY_HIGH, etc). + // A higher priority will encourage notification manager to expand it. + notificationBuilder.setPriority(priority) + } + return notificationBuilder.getNotification() + } } -} +} \ No newline at end of file -- cgit v1.2.3 From ff4ce6a78dd407dafe66512a548be17b8efeda4a Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 11 Jun 2021 18:16:44 +0000 Subject: AOSP/Calendar - Copy of AlertService.java .kt file The Java code in AlertService.java has been copied into a new file named AlarmManagerInterface.kt. Test: manual - opening both files shows they are identical. Change-Id: I463119ec9a66f09c69b508ced51d85b2cab277f1 --- src/com/android/calendar/alerts/AlertService.kt | 202 ++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 src/com/android/calendar/alerts/AlertService.kt diff --git a/src/com/android/calendar/alerts/AlertService.kt b/src/com/android/calendar/alerts/AlertService.kt new file mode 100644 index 00000000..d2c994da --- /dev/null +++ b/src/com/android/calendar/alerts/AlertService.kt @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2008 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.alerts; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.Service; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.provider.CalendarContract; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.CalendarAlerts; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Log; + +import com.android.calendar.R; +import com.android.calendar.Utils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.TimeZone; + +/** + * This service is used to handle calendar event reminders. + */ +public class AlertService extends Service { + static final boolean DEBUG = true; + private static final String TAG = "AlertService"; + + private volatile Looper mServiceLooper; + + static final String[] ALERT_PROJECTION = new String[] { + CalendarAlerts._ID, // 0 + CalendarAlerts.EVENT_ID, // 1 + CalendarAlerts.STATE, // 2 + CalendarAlerts.TITLE, // 3 + CalendarAlerts.EVENT_LOCATION, // 4 + CalendarAlerts.SELF_ATTENDEE_STATUS, // 5 + CalendarAlerts.ALL_DAY, // 6 + CalendarAlerts.ALARM_TIME, // 7 + CalendarAlerts.MINUTES, // 8 + CalendarAlerts.BEGIN, // 9 + CalendarAlerts.END, // 10 + CalendarAlerts.DESCRIPTION, // 11 + }; + + 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; + private static final int ALERT_INDEX_TITLE = 3; + private static final int ALERT_INDEX_EVENT_LOCATION = 4; + private static final int ALERT_INDEX_SELF_ATTENDEE_STATUS = 5; + private static final int ALERT_INDEX_ALL_DAY = 6; + private static final int ALERT_INDEX_ALARM_TIME = 7; + private static final int ALERT_INDEX_MINUTES = 8; + private static final int ALERT_INDEX_BEGIN = 9; + private static final int ALERT_INDEX_END = 10; + private static final int ALERT_INDEX_DESCRIPTION = 11; + + private static final String ACTIVE_ALERTS_SELECTION = "(" + CalendarAlerts.STATE + "=? OR " + + CalendarAlerts.STATE + "=?) AND " + CalendarAlerts.ALARM_TIME + "<="; + + private static final String[] ACTIVE_ALERTS_SELECTION_ARGS = new String[] { + Integer.toString(CalendarAlerts.STATE_FIRED), + Integer.toString(CalendarAlerts.STATE_SCHEDULED) + }; + + private static final String ACTIVE_ALERTS_SORT = "begin DESC, end DESC"; + + private static final String DISMISS_OLD_SELECTION = CalendarAlerts.END + " mNw; + + public NotificationWrapper(Notification n, int notificationId, long eventId, + long startMillis, long endMillis, boolean doPopup) { + mNotification = n; + mEventId = eventId; + mBegin = startMillis; + mEnd = endMillis; + + // popup? + // notification id? + } + + public NotificationWrapper(Notification n) { + mNotification = n; + } + + public void add(NotificationWrapper nw) { + if (mNw == null) { + mNw = new ArrayList(); + } + mNw.add(nw); + } + } + + // Added wrapper for testing + public static class NotificationMgrWrapper extends NotificationMgr { + NotificationManager mNm; + + public NotificationMgrWrapper(NotificationManager nm) { + mNm = nm; + } + + @Override + public void cancel(int id) { + mNm.cancel(id); + } + + @Override + public void notify(int id, NotificationWrapper nw) { + mNm.notify(id, nw.mNotification); + } + } + + static class NotificationInfo { + String eventName; + String location; + String description; + long startMillis; + long endMillis; + long eventId; + boolean allDay; + boolean newAlert; + + NotificationInfo(String eventName, String location, String description, long startMillis, + long endMillis, long eventId, boolean allDay, boolean newAlert) { + this.eventName = eventName; + this.location = location; + this.description = description; + this.startMillis = startMillis; + this.endMillis = endMillis; + this.eventId = eventId; + this.newAlert = newAlert; + this.allDay = allDay; + } + } + + @Override + public void onCreate() { + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return START_REDELIVER_INTENT; + } + + @Override + public void onDestroy() { + mServiceLooper.quit(); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} -- cgit v1.2.3 From b86cec11644624405aa22d9db5be489f28fa6721 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Fri, 11 Jun 2021 19:46:35 +0000 Subject: AOSP/Calendar - Add Kotlin copy of the following files: EventGeometry.java, MultiStateButton.java Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: Ib59fcd2ee97215040b28d4ccb8d961a3415222eb --- src/com/android/calendar/EventGeometry.kt | 170 ++++++++++++++++++++++++ src/com/android/calendar/MultiStateButton.kt | 192 +++++++++++++++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 src/com/android/calendar/EventGeometry.kt create mode 100644 src/com/android/calendar/MultiStateButton.kt diff --git a/src/com/android/calendar/EventGeometry.kt b/src/com/android/calendar/EventGeometry.kt new file mode 100644 index 00000000..cdecb49c --- /dev/null +++ b/src/com/android/calendar/EventGeometry.kt @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2008 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.graphics.Rect; + +public class EventGeometry { + // This is the space from the grid line to the event rectangle. + private int mCellMargin = 0; + + private float mMinuteHeight; + + private float mHourGap; + private float mMinEventHeight; + + void setCellMargin(int cellMargin) { + mCellMargin = cellMargin; + } + + public void setHourGap(float gap) { + mHourGap = gap; + } + + public void setMinEventHeight(float height) { + mMinEventHeight = height; + } + + public void setHourHeight(float height) { + mMinuteHeight = height / 60.0f; + } + + // Computes the rectangle coordinates of the given event on the screen. + // Returns true if the rectangle is visible on the screen. + public boolean computeEventRect(int date, int left, int top, int cellWidth, Event event) { + if (event.drawAsAllday()) { + return false; + } + + float cellMinuteHeight = mMinuteHeight; + int startDay = event.startDay; + int endDay = event.endDay; + + if (startDay > date || endDay < date) { + return false; + } + + int startTime = event.startTime; + int endTime = event.endTime; + + // If the event started on a previous day, then show it starting + // at the beginning of this day. + if (startDay < date) { + startTime = 0; + } + + // If the event ends on a future day, then show it extending to + // the end of this day. + if (endDay > date) { + endTime = DayView.MINUTES_PER_DAY; + } + + int col = event.getColumn(); + int maxCols = event.getMaxColumns(); + int startHour = startTime / 60; + int endHour = endTime / 60; + + // If the end point aligns on a cell boundary then count it as + // ending in the previous cell so that we don't cross the border + // between hours. + if (endHour * 60 == endTime) + endHour -= 1; + + event.top = top; + event.top += (int) (startTime * cellMinuteHeight); + event.top += startHour * mHourGap; + + event.bottom = top; + event.bottom += (int) (endTime * cellMinuteHeight); + event.bottom += endHour * mHourGap - 1; + + // Make the rectangle be at least mMinEventHeight pixels high + if (event.bottom < event.top + mMinEventHeight) { + event.bottom = event.top + mMinEventHeight; + } + + float colWidth = (float) (cellWidth - (maxCols + 1) * mCellMargin) / (float) maxCols; + event.left = left + col * (colWidth + mCellMargin); + event.right = event.left + colWidth; + return true; + } + + /** + * Returns true if this event intersects the selection region. + */ + boolean eventIntersectsSelection(Event event, Rect selection) { + if (event.left < selection.right && event.right >= selection.left + && event.top < selection.bottom && event.bottom >= selection.top) { + return true; + } + return false; + } + + /** + * Computes the distance from the given point to the given event. + */ + float pointToEvent(float x, float y, Event event) { + float left = event.left; + float right = event.right; + float top = event.top; + float bottom = event.bottom; + + if (x >= left) { + if (x <= right) { + if (y >= top) { + if (y <= bottom) { + // x,y is inside the event rectangle + return 0f; + } + // x,y is below the event rectangle + return y - bottom; + } + // x,y is above the event rectangle + return top - y; + } + + // x > right + float dx = x - right; + if (y < top) { + // the upper right corner + float dy = top - y; + return (float) Math.sqrt(dx * dx + dy * dy); + } + if (y > bottom) { + // the lower right corner + float dy = y - bottom; + return (float) Math.sqrt(dx * dx + dy * dy); + } + // x,y is to the right of the event rectangle + return dx; + } + // x < left + float dx = left - x; + if (y < top) { + // the upper left corner + float dy = top - y; + return (float) Math.sqrt(dx * dx + dy * dy); + } + if (y > bottom) { + // the lower left corner + float dy = y - bottom; + return (float) Math.sqrt(dx * dx + dy * dy); + } + // x,y is to the left of the event rectangle + return dx; + } +} diff --git a/src/com/android/calendar/MultiStateButton.kt b/src/com/android/calendar/MultiStateButton.kt new file mode 100644 index 00000000..8034b28e --- /dev/null +++ b/src/com/android/calendar/MultiStateButton.kt @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2010 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.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; +import android.widget.Button; + +/** + *

+ * A button with more than two states. When the button is pressed + * or clicked, the state transitions automatically. + *

+ * + *

XML attributes

+ *

+ * See {@link R.styleable#MultiStateButton + * MultiStateButton Attributes}, {@link android.R.styleable#Button Button + * Attributes}, {@link android.R.styleable#TextView TextView Attributes}, {@link + * android.R.styleable#View View Attributes} + *

+ */ + +public class MultiStateButton extends Button { + //The current state for this button, ranging from 0 to maxState-1 + private int mState; + //The maximum number of states allowed for this button. + private int mMaxStates; + //The currently displaying resource ID. This gets set to a default on creation and remains + //on the last set if the resources get set to null. + private int mButtonResource; + //A list of all drawable resources used by this button in the order it uses them. + private int[] mButtonResources; + private Drawable mButtonDrawable; + + public MultiStateButton(Context context) { + this(context, null); + } + + public MultiStateButton(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public MultiStateButton(Context context, AttributeSet attrs, int defStyle) { + //Currently using the standard buttonStyle, will update when new resources are added. + super(context, attrs, defStyle); + mMaxStates = 1; + mState = 0; + //TODO add a more generic default button + mButtonResources = new int[] { R.drawable.widget_show }; + setButtonDrawable(mButtonResources[mState]); + } + + @Override + public boolean performClick() { + /* When clicked, toggle the state */ + transitionState(); + return super.performClick(); + } + + public void transitionState() { + mState = (mState + 1) % mMaxStates; + setButtonDrawable(mButtonResources[mState]); + } + + /** + * Allows for a new set of drawable resource ids to be set. + * + * This sets the maximum states allowed to the length of the resources array. It will also + * set the current state to the maximum allowed if it's greater than the new max. + */ + public void setButtonResources(int[] resources) throws IllegalArgumentException { + if(resources == null) { + throw new IllegalArgumentException("Button resources cannot be null"); + } + mMaxStates = resources.length; + if(mState >= mMaxStates) { + mState = mMaxStates - 1; + } + mButtonResources = resources; + } + + /** + * Attempts to set the state. Returns true if successful, false otherwise. + */ + public boolean setState(int state){ + if(state >= mMaxStates || state < 0) { + //When moved out of Calendar the tag should be changed. + Log.w("Cal", "MultiStateButton state set to value greater than maxState or < 0"); + return false; + } + mState = state; + setButtonDrawable(mButtonResources[mState]); + return true; + } + + public int getState() { + return mState; + } + + /** + * Set the background to a given Drawable, identified by its resource id. + * + * @param resid the resource id of the drawable to use as the background + */ + public void setButtonDrawable(int resid) { + if (resid != 0 && resid == mButtonResource) { + return; + } + + mButtonResource = resid; + + Drawable d = null; + if (mButtonResource != 0) { + d = getResources().getDrawable(mButtonResource); + } + setButtonDrawable(d); + } + + /** + * Set the background to a given Drawable + * + * @param d The Drawable to use as the background + */ + public void setButtonDrawable(Drawable d) { + if (d != null) { + if (mButtonDrawable != null) { + mButtonDrawable.setCallback(null); + unscheduleDrawable(mButtonDrawable); + } + d.setCallback(this); + d.setState(getDrawableState()); + d.setVisible(getVisibility() == VISIBLE, false); + mButtonDrawable = d; + mButtonDrawable.setState(null); + setMinHeight(mButtonDrawable.getIntrinsicHeight()); + setWidth(mButtonDrawable.getIntrinsicWidth()); + } + refreshDrawableState(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (mButtonDrawable != null) { + final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; + final int horizontalGravity = getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK; + final int height = mButtonDrawable.getIntrinsicHeight(); + final int width = mButtonDrawable.getIntrinsicWidth(); + + int y = 0; + int x = 0; + + switch (verticalGravity) { + case Gravity.BOTTOM: + y = getHeight() - height; + break; + case Gravity.CENTER_VERTICAL: + y = (getHeight() - height) / 2; + break; + } + switch (horizontalGravity) { + case Gravity.RIGHT: + x = getWidth() - width; + break; + case Gravity.CENTER_HORIZONTAL: + x = (getWidth() - width) / 2; + break; + } + + mButtonDrawable.setBounds(x, y, x + width, y + height); + mButtonDrawable.draw(canvas); + } + } +} -- cgit v1.2.3 From 222b48467ade929c820c57848c126088bf5d9d40 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 11 Jun 2021 19:48:07 +0000 Subject: AOSP/Calendar - AlertService.java fully converted with bp file Using Android Studio, AlertService.java has been converted to Kotlin. The Android.bp file has also been added to in order to accommodate for the new file. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.18 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 6m 25s Total aggregated tests run time: 6m 25s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.98 tests/sec [376 tests / 385276 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 7523 ms || clean = 2206 ms Total preparation time: 7s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 7m 55s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: If66e78558e58212b528f6af3dee7b8f17a62f99c --- Android.bp | 1 + src/com/android/calendar/alerts/AlertService.kt | 263 ++++++++++-------------- 2 files changed, 115 insertions(+), 149 deletions(-) diff --git a/Android.bp b/Android.bp index 0e825764..9bff6900 100644 --- a/Android.bp +++ b/Android.bp @@ -18,6 +18,7 @@ license { // Include res dir from chips exclude_srcsd = [ "src/**/calendar/alerts/AlertUtils.java", + "src/**/calendar/alerts/AlertService.java", "src/**/calendar/DayOfMonthDrawable.java", ] diff --git a/src/com/android/calendar/alerts/AlertService.kt b/src/com/android/calendar/alerts/AlertService.kt index d2c994da..2cb9a268 100644 --- a/src/com/android/calendar/alerts/AlertService.kt +++ b/src/com/android/calendar/alerts/AlertService.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,190 +13,155 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar.alerts -package com.android.calendar.alerts; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.Service; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.Process; -import android.provider.CalendarContract; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.CalendarAlerts; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.TimeZone; +import android.app.Notification +import android.app.NotificationManager +import android.app.Service +import android.content.Intent +import android.os.IBinder +import android.os.Looper +import android.provider.CalendarContract.CalendarAlerts +import java.util.ArrayList /** * This service is used to handle calendar event reminders. */ -public class AlertService extends Service { - static final boolean DEBUG = true; - private static final String TAG = "AlertService"; - - private volatile Looper mServiceLooper; - - static final String[] ALERT_PROJECTION = new String[] { - CalendarAlerts._ID, // 0 - CalendarAlerts.EVENT_ID, // 1 - CalendarAlerts.STATE, // 2 - CalendarAlerts.TITLE, // 3 - CalendarAlerts.EVENT_LOCATION, // 4 - CalendarAlerts.SELF_ATTENDEE_STATUS, // 5 - CalendarAlerts.ALL_DAY, // 6 - CalendarAlerts.ALARM_TIME, // 7 - CalendarAlerts.MINUTES, // 8 - CalendarAlerts.BEGIN, // 9 - CalendarAlerts.END, // 10 - CalendarAlerts.DESCRIPTION, // 11 - }; - - 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; - private static final int ALERT_INDEX_TITLE = 3; - private static final int ALERT_INDEX_EVENT_LOCATION = 4; - private static final int ALERT_INDEX_SELF_ATTENDEE_STATUS = 5; - private static final int ALERT_INDEX_ALL_DAY = 6; - private static final int ALERT_INDEX_ALARM_TIME = 7; - private static final int ALERT_INDEX_MINUTES = 8; - private static final int ALERT_INDEX_BEGIN = 9; - private static final int ALERT_INDEX_END = 10; - private static final int ALERT_INDEX_DESCRIPTION = 11; - - private static final String ACTIVE_ALERTS_SELECTION = "(" + CalendarAlerts.STATE + "=? OR " - + CalendarAlerts.STATE + "=?) AND " + CalendarAlerts.ALARM_TIME + "<="; - - private static final String[] ACTIVE_ALERTS_SELECTION_ARGS = new String[] { - Integer.toString(CalendarAlerts.STATE_FIRED), - Integer.toString(CalendarAlerts.STATE_SCHEDULED) - }; - - private static final String ACTIVE_ALERTS_SORT = "begin DESC, end DESC"; - - private static final String DISMISS_OLD_SELECTION = CalendarAlerts.END + " mNw; - - public NotificationWrapper(Notification n, int notificationId, long eventId, - long startMillis, long endMillis, boolean doPopup) { - mNotification = n; - mEventId = eventId; - mBegin = startMillis; - mEnd = endMillis; + class NotificationWrapper { + var mNotification: Notification + var mEventId: Long = 0 + var mBegin: Long = 0 + var mEnd: Long = 0 + var mNw: ArrayList? = null + + constructor( + n: Notification, + notificationId: Int, + eventId: Long, + startMillis: Long, + endMillis: Long, + doPopup: Boolean + ) { + mNotification = n + mEventId = eventId + mBegin = startMillis + mEnd = endMillis // popup? // notification id? } - public NotificationWrapper(Notification n) { - mNotification = n; + constructor(n: Notification) { + mNotification = n } - public void add(NotificationWrapper nw) { - if (mNw == null) { - mNw = new ArrayList(); + fun add(nw: NotificationWrapper?) { + val temp = mNw + if (temp == null) { + mNw = ArrayList() } - mNw.add(nw); + mNw?.add(nw as AlertService.NotificationWrapper) } } // Added wrapper for testing - public static class NotificationMgrWrapper extends NotificationMgr { - NotificationManager mNm; - - public NotificationMgrWrapper(NotificationManager nm) { - mNm = nm; - } - + class NotificationMgrWrapper(nm: NotificationManager) : NotificationMgr() { + var mNm: NotificationManager @Override - public void cancel(int id) { - mNm.cancel(id); + override fun cancel(id: Int) { + mNm.cancel(id) } @Override - public void notify(int id, NotificationWrapper nw) { - mNm.notify(id, nw.mNotification); + override fun notify(id: Int, nw: NotificationWrapper) { + mNm.notify(id, nw.mNotification) } - } - static class NotificationInfo { - String eventName; - String location; - String description; - long startMillis; - long endMillis; - long eventId; - boolean allDay; - boolean newAlert; - - NotificationInfo(String eventName, String location, String description, long startMillis, - long endMillis, long eventId, boolean allDay, boolean newAlert) { - this.eventName = eventName; - this.location = location; - this.description = description; - this.startMillis = startMillis; - this.endMillis = endMillis; - this.eventId = eventId; - this.newAlert = newAlert; - this.allDay = allDay; + init { + mNm = nm } } + internal class NotificationInfo( + var eventName: String, + var location: String, + var description: String, + var startMillis: Long, + var endMillis: Long, + var eventId: Long, + var allDay: Boolean, + var newAlert: Boolean + ) + @Override - public void onCreate() { + override fun onCreate() { } @Override - public int onStartCommand(Intent intent, int flags, int startId) { - return START_REDELIVER_INTENT; + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + return START_REDELIVER_INTENT } @Override - public void onDestroy() { - mServiceLooper.quit(); + override fun onDestroy() { + mServiceLooper?.quit() } @Override - public IBinder onBind(Intent intent) { - return null; + override fun onBind(intent: Intent?): IBinder? { + return null + } + + companion object { + const val DEBUG = true + private const val TAG = "AlertService" + val ALERT_PROJECTION = arrayOf( + CalendarAlerts._ID, // 0 + CalendarAlerts.EVENT_ID, // 1 + CalendarAlerts.STATE, // 2 + CalendarAlerts.TITLE, // 3 + CalendarAlerts.EVENT_LOCATION, // 4 + CalendarAlerts.SELF_ATTENDEE_STATUS, // 5 + CalendarAlerts.ALL_DAY, // 6 + CalendarAlerts.ALARM_TIME, // 7 + CalendarAlerts.MINUTES, // 8 + CalendarAlerts.BEGIN, // 9 + CalendarAlerts.END, // 10 + CalendarAlerts.DESCRIPTION + ) + private const val ALERT_INDEX_ID = 0 + private const val ALERT_INDEX_EVENT_ID = 1 + private const val ALERT_INDEX_STATE = 2 + private const val ALERT_INDEX_TITLE = 3 + private const val ALERT_INDEX_EVENT_LOCATION = 4 + private const val ALERT_INDEX_SELF_ATTENDEE_STATUS = 5 + private const val ALERT_INDEX_ALL_DAY = 6 + private const val ALERT_INDEX_ALARM_TIME = 7 + private const val ALERT_INDEX_MINUTES = 8 + private const val ALERT_INDEX_BEGIN = 9 + private const val ALERT_INDEX_END = 10 + private const val ALERT_INDEX_DESCRIPTION = 11 + private val ACTIVE_ALERTS_SELECTION = ("(" + CalendarAlerts.STATE.toString() + "=? OR " + + CalendarAlerts.STATE.toString() + "=?) AND " + + CalendarAlerts.ALARM_TIME.toString() + "<=") + private val ACTIVE_ALERTS_SELECTION_ARGS = arrayOf( + Integer.toString(CalendarAlerts.STATE_FIRED), + Integer.toString(CalendarAlerts.STATE_SCHEDULED) + ) + private const val ACTIVE_ALERTS_SORT = "begin DESC, end DESC" + private val DISMISS_OLD_SELECTION: String = (CalendarAlerts.END.toString() + " Date: Fri, 11 Jun 2021 21:32:38 +0000 Subject: AOSP/Calendar - Add Kotlin code for EventGeometry.kt and MultiStateButton.kt. Also uploaded corresponding Android.bp file Test: manual (ran build and unit tests) and CTS testing suite $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.161 OK (22 tests) CTS TESTING: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ============================================ ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 9m 41s Total aggregated tests run time: 9m 41s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.65 tests/sec [376 tests / 581327 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 12340 ms || clean = 2778 ms Total preparation time: 12s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 11m 52s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ BUG: 189873985 Change-Id: Ibc141715d9c4e60746056bffab39ac8216717edb --- Android.bp | 2 + src/com/android/calendar/EventGeometry.kt | 154 +++++++++----------- src/com/android/calendar/MultiStateButton.kt | 204 ++++++++++++--------------- 3 files changed, 160 insertions(+), 200 deletions(-) diff --git a/Android.bp b/Android.bp index 0e825764..b73c3b59 100644 --- a/Android.bp +++ b/Android.bp @@ -25,7 +25,9 @@ exclude_srcsm = [ "src/**/calendar/AsyncQueryServiceHelper.java", "src/**/calendar/CalendarApplication.java", "src/**/calendar/CalendarBackupAgent.java", + "src/**/calendar/EventGeometry.java", "src/**/calendar/GoogleCalendarUriIntentFilter.java", + "src/**/calendar/MultiStateButton.java", "src/**/calendar/UpgradeReceiver.java", ] diff --git a/src/com/android/calendar/EventGeometry.kt b/src/com/android/calendar/EventGeometry.kt index cdecb49c..b6af49a0 100644 --- a/src/com/android/calendar/EventGeometry.kt +++ b/src/com/android/calendar/EventGeometry.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,158 +13,142 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar -package com.android.calendar; +import android.graphics.Rect -import android.graphics.Rect; - -public class EventGeometry { +class EventGeometry { // This is the space from the grid line to the event rectangle. - private int mCellMargin = 0; - - private float mMinuteHeight; - - private float mHourGap; - private float mMinEventHeight; - - void setCellMargin(int cellMargin) { - mCellMargin = cellMargin; + private var mCellMargin = 0 + private var mMinuteHeight = 0f + private var mHourGap = 0f + private var mMinEventHeight = 0f + fun setCellMargin(cellMargin: Int) { + mCellMargin = cellMargin } - public void setHourGap(float gap) { - mHourGap = gap; + fun setHourGap(gap: Float) { + mHourGap = gap } - public void setMinEventHeight(float height) { - mMinEventHeight = height; + fun setMinEventHeight(height: Float) { + mMinEventHeight = height } - public void setHourHeight(float height) { - mMinuteHeight = height / 60.0f; + fun setHourHeight(height: Float) { + mMinuteHeight = height / 60.0f } // Computes the rectangle coordinates of the given event on the screen. // Returns true if the rectangle is visible on the screen. - public boolean computeEventRect(int date, int left, int top, int cellWidth, Event event) { + fun computeEventRect(date: Int, left: Int, top: Int, cellWidth: Int, event: Event): Boolean { if (event.drawAsAllday()) { - return false; + return false } - - float cellMinuteHeight = mMinuteHeight; - int startDay = event.startDay; - int endDay = event.endDay; - + val cellMinuteHeight = mMinuteHeight + val startDay: Int = event.startDay + val endDay: Int = event.endDay if (startDay > date || endDay < date) { - return false; + return false } - - int startTime = event.startTime; - int endTime = event.endTime; + var startTime: Int = event.startTime + var endTime: Int = event.endTime // If the event started on a previous day, then show it starting // at the beginning of this day. if (startDay < date) { - startTime = 0; + startTime = 0 } // If the event ends on a future day, then show it extending to // the end of this day. if (endDay > date) { - endTime = DayView.MINUTES_PER_DAY; + endTime = DayView.MINUTES_PER_DAY } - - int col = event.getColumn(); - int maxCols = event.getMaxColumns(); - int startHour = startTime / 60; - int endHour = endTime / 60; + val col: Int = event.getColumn() + val maxCols: Int = event.getMaxColumns() + val startHour = startTime / 60 + var endHour = endTime / 60 // If the end point aligns on a cell boundary then count it as // ending in the previous cell so that we don't cross the border // between hours. - if (endHour * 60 == endTime) - endHour -= 1; - - event.top = top; - event.top += (int) (startTime * cellMinuteHeight); - event.top += startHour * mHourGap; - - event.bottom = top; - event.bottom += (int) (endTime * cellMinuteHeight); - event.bottom += endHour * mHourGap - 1; + if (endHour * 60 == endTime) endHour -= 1 + event.top = top as Float + event.top += (startTime * cellMinuteHeight).toInt() + event.top += startHour * mHourGap + event.bottom = top as Float + event.bottom += (endTime * cellMinuteHeight).toInt() + event.bottom += endHour * mHourGap - 1 // Make the rectangle be at least mMinEventHeight pixels high if (event.bottom < event.top + mMinEventHeight) { - event.bottom = event.top + mMinEventHeight; + event.bottom = event.top + mMinEventHeight } - - float colWidth = (float) (cellWidth - (maxCols + 1) * mCellMargin) / (float) maxCols; - event.left = left + col * (colWidth + mCellMargin); - event.right = event.left + colWidth; - return true; + val colWidth = (cellWidth - (maxCols + 1) * mCellMargin).toFloat() / maxCols.toFloat() + event.left = left + col * (colWidth + mCellMargin) + event.right = event.left + colWidth + return true } /** * Returns true if this event intersects the selection region. */ - boolean eventIntersectsSelection(Event event, Rect selection) { - if (event.left < selection.right && event.right >= selection.left - && event.top < selection.bottom && event.bottom >= selection.top) { - return true; - } - return false; + fun eventIntersectsSelection(event: Event, selection: Rect): Boolean { + return if (event.left < selection.right && event.right >= selection.left && + event.top < selection.bottom && event.bottom >= selection.top) { + true + } else false } /** * Computes the distance from the given point to the given event. */ - float pointToEvent(float x, float y, Event event) { - float left = event.left; - float right = event.right; - float top = event.top; - float bottom = event.bottom; - + fun pointToEvent(x: Float, y: Float, event: Event): Float { + val left: Float = event.left + val right: Float = event.right + val top: Float = event.top + val bottom: Float = event.bottom if (x >= left) { if (x <= right) { - if (y >= top) { + return if (y >= top) { if (y <= bottom) { // x,y is inside the event rectangle - return 0f; - } + 0f + } else y - bottom // x,y is below the event rectangle - return y - bottom; - } + } else top - y // x,y is above the event rectangle - return top - y; } // x > right - float dx = x - right; + val dx = x - right if (y < top) { // the upper right corner - float dy = top - y; - return (float) Math.sqrt(dx * dx + dy * dy); + val dy = top - y + return (Math.sqrt(dx as Double * dx + dy as Double * dy)) as Float } if (y > bottom) { // the lower right corner - float dy = y - bottom; - return (float) Math.sqrt(dx * dx + dy * dy); + val dy = y - bottom + return (Math.sqrt(dx as Double * dx + dy as Double * dy)) as Float } // x,y is to the right of the event rectangle - return dx; + return dx } // x < left - float dx = left - x; + val dx = left - x if (y < top) { // the upper left corner - float dy = top - y; - return (float) Math.sqrt(dx * dx + dy * dy); + val dy = top - y + return (Math.sqrt(dx as Double * dx + dy as Double * dy)) as Float } if (y > bottom) { // the lower left corner - float dy = y - bottom; - return (float) Math.sqrt(dx * dx + dy * dy); + val dy = y - bottom + return (Math.sqrt(dx as Double * dx + dy as Double * dy)) as Float } // x,y is to the left of the event rectangle - return dx; + return dx } -} +} \ No newline at end of file diff --git a/src/com/android/calendar/MultiStateButton.kt b/src/com/android/calendar/MultiStateButton.kt index 8034b28e..f86ee6ba 100644 --- a/src/com/android/calendar/MultiStateButton.kt +++ b/src/com/android/calendar/MultiStateButton.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,71 +13,55 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.calendar; +package com.android.calendar -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Gravity; -import android.widget.Button; +import android.content.Context +import android.graphics.Canvas +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.util.Log +import android.view.Gravity +import android.widget.Button /** - *

* A button with more than two states. When the button is pressed * or clicked, the state transitions automatically. - *

* - *

XML attributes

- *

- * See {@link R.styleable#MultiStateButton - * MultiStateButton Attributes}, {@link android.R.styleable#Button Button - * Attributes}, {@link android.R.styleable#TextView TextView Attributes}, {@link - * android.R.styleable#View View Attributes} - *

+ * **XML attributes** + * See [ MultiStateButton Attributes][R.styleable.MultiStateButton], + * [Button][android.R.styleable.Button], [TextView Attributes][android.R.styleable.TextView], + * [ ][android.R.styleable.View] + * */ - -public class MultiStateButton extends Button { +class MultiStateButton(context: Context?, attrs: AttributeSet?, defStyle: Int) : + Button(context, attrs, defStyle) { //The current state for this button, ranging from 0 to maxState-1 - private int mState; + var mState = 0 + private set + //The maximum number of states allowed for this button. - private int mMaxStates; + private var mMaxStates = 1 + //The currently displaying resource ID. This gets set to a default on creation and remains //on the last set if the resources get set to null. - private int mButtonResource; - //A list of all drawable resources used by this button in the order it uses them. - private int[] mButtonResources; - private Drawable mButtonDrawable; - - public MultiStateButton(Context context) { - this(context, null); - } + private var mButtonResource = 0 - public MultiStateButton(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } + //A list of all drawable resources used by this button in the order it uses them. + private var mButtonResources: IntArray + private var mButtonDrawable: Drawable? = null - public MultiStateButton(Context context, AttributeSet attrs, int defStyle) { - //Currently using the standard buttonStyle, will update when new resources are added. - super(context, attrs, defStyle); - mMaxStates = 1; - mState = 0; - //TODO add a more generic default button - mButtonResources = new int[] { R.drawable.widget_show }; - setButtonDrawable(mButtonResources[mState]); - } + constructor(context: Context?) : this(context, null) {} + constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0) {} - @Override - public boolean performClick() { + override fun performClick(): Boolean { /* When clicked, toggle the state */ - transitionState(); - return super.performClick(); + transitionState() + return super.performClick() } - public void transitionState() { - mState = (mState + 1) % mMaxStates; - setButtonDrawable(mButtonResources[mState]); + fun transitionState() { + mState = (mState + 1) % mMaxStates + setButtonDrawable(mButtonResources[mState]) } /** @@ -86,33 +70,30 @@ public class MultiStateButton extends Button { * This sets the maximum states allowed to the length of the resources array. It will also * set the current state to the maximum allowed if it's greater than the new max. */ - public void setButtonResources(int[] resources) throws IllegalArgumentException { - if(resources == null) { - throw new IllegalArgumentException("Button resources cannot be null"); + //@Throws(IllegalArgumentException::class) + fun setButtonResources(resources: IntArray?) { + if (resources == null) { + throw IllegalArgumentException("Button resources cannot be null") } - mMaxStates = resources.length; - if(mState >= mMaxStates) { - mState = mMaxStates - 1; + mMaxStates = resources.size + if (mState >= mMaxStates) { + mState = mMaxStates - 1 } - mButtonResources = resources; + mButtonResources = resources } /** * Attempts to set the state. Returns true if successful, false otherwise. */ - public boolean setState(int state){ - if(state >= mMaxStates || state < 0) { + fun setState(state: Int): Boolean { + if (state >= mMaxStates || state < 0) { //When moved out of Calendar the tag should be changed. - Log.w("Cal", "MultiStateButton state set to value greater than maxState or < 0"); - return false; + Log.w("Cal", "MultiStateButton state set to value greater than maxState or < 0") + return false } - mState = state; - setButtonDrawable(mButtonResources[mState]); - return true; - } - - public int getState() { - return mState; + mState = state + setButtonDrawable(mButtonResources[mState]) + return true } /** @@ -120,18 +101,16 @@ public class MultiStateButton extends Button { * * @param resid the resource id of the drawable to use as the background */ - public void setButtonDrawable(int resid) { + fun setButtonDrawable(resid: Int) { if (resid != 0 && resid == mButtonResource) { - return; + return } - - mButtonResource = resid; - - Drawable d = null; + mButtonResource = resid + var d: Drawable? = null if (mButtonResource != 0) { - d = getResources().getDrawable(mButtonResource); + d = getResources().getDrawable(mButtonResource) } - setButtonDrawable(d); + setButtonDrawable(d) } /** @@ -139,54 +118,49 @@ public class MultiStateButton extends Button { * * @param d The Drawable to use as the background */ - public void setButtonDrawable(Drawable d) { + fun setButtonDrawable(d: Drawable?) { if (d != null) { if (mButtonDrawable != null) { - mButtonDrawable.setCallback(null); - unscheduleDrawable(mButtonDrawable); + mButtonDrawable?.setCallback(null) + unscheduleDrawable(mButtonDrawable) } - d.setCallback(this); - d.setState(getDrawableState()); - d.setVisible(getVisibility() == VISIBLE, false); - mButtonDrawable = d; - mButtonDrawable.setState(null); - setMinHeight(mButtonDrawable.getIntrinsicHeight()); - setWidth(mButtonDrawable.getIntrinsicWidth()); + d.setCallback(this) + d.setState(getDrawableState()) + d.setVisible(getVisibility() === VISIBLE, false) + mButtonDrawable = d + mButtonDrawable?.setState(getDrawableState()) + setMinHeight(mButtonDrawable?.getIntrinsicHeight() ?: 0) + setWidth(mButtonDrawable?.getIntrinsicWidth() ?: 0) } - refreshDrawableState(); + refreshDrawableState() } - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); + protected override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) if (mButtonDrawable != null) { - final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; - final int horizontalGravity = getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK; - final int height = mButtonDrawable.getIntrinsicHeight(); - final int width = mButtonDrawable.getIntrinsicWidth(); - - int y = 0; - int x = 0; - - switch (verticalGravity) { - case Gravity.BOTTOM: - y = getHeight() - height; - break; - case Gravity.CENTER_VERTICAL: - y = (getHeight() - height) / 2; - break; + val verticalGravity: Int = getGravity() and Gravity.VERTICAL_GRAVITY_MASK + val horizontalGravity: Int = getGravity() and Gravity.HORIZONTAL_GRAVITY_MASK + val height: Int = mButtonDrawable?.getIntrinsicHeight() ?: 0 + val width: Int = mButtonDrawable?.getIntrinsicWidth() ?: 0 + var y = 0 + var x = 0 + when (verticalGravity) { + Gravity.BOTTOM -> y = getHeight() - height + Gravity.CENTER_VERTICAL -> y = (getHeight() - height) / 2 } - switch (horizontalGravity) { - case Gravity.RIGHT: - x = getWidth() - width; - break; - case Gravity.CENTER_HORIZONTAL: - x = (getWidth() - width) / 2; - break; + when (horizontalGravity) { + Gravity.RIGHT -> x = getWidth() - width + Gravity.CENTER_HORIZONTAL -> x = (getWidth() - width) / 2 } - - mButtonDrawable.setBounds(x, y, x + width, y + height); - mButtonDrawable.draw(canvas); + mButtonDrawable?.setBounds(x, y, x + width, y + height) + mButtonDrawable?.draw(canvas) } } -} + + init { + //Currently using the standard buttonStyle, will update when new resources are added. + //TODO add a more generic default button + mButtonResources = intArrayOf(R.drawable.widget_show) + setButtonDrawable(mButtonResources[mState]) + } +} \ No newline at end of file -- cgit v1.2.3 From 792018a765f834e6d1b5c3721a5498b87378e270 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 10 Jun 2021 21:16:24 +0000 Subject: AOSP/Calendar - Copy of AlarmManagerInterface.java .kt file The Java code in AlarmManagerInterface.java has been copied into a new file named AlarmManagerInterface.kt. Test: manual - opening both files shows they are identical. Change-Id: I1b9991f3f891999267c965aa6268fb6e280f937c --- .../calendar/alerts/AlarmManagerInterface.kt | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/com/android/calendar/alerts/AlarmManagerInterface.kt diff --git a/src/com/android/calendar/alerts/AlarmManagerInterface.kt b/src/com/android/calendar/alerts/AlarmManagerInterface.kt new file mode 100644 index 00000000..3c66434d --- /dev/null +++ b/src/com/android/calendar/alerts/AlarmManagerInterface.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012 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.alerts; + +import android.app.PendingIntent; + +/** + * AlarmManager abstracted to an interface for testability. + */ +public interface AlarmManagerInterface { + public void set(int type, long triggerAtMillis, PendingIntent operation); +} -- cgit v1.2.3 From d2ceaee7c4b737f453defdc8c413762b929976e7 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 10 Jun 2021 21:26:35 +0000 Subject: AOSP/Calendar - AlarmManagerInterface.java fully converted with bp file Using Android Studio, AlarmManagerInterface.java has been converted to Kotlin. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.151 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 12m 53s Total aggregated tests run time: 12m 53s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.49 tests/sec [376 tests / 773497 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 7789 ms || clean = 2001 ms Total preparation time: 7s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 14m 24s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: Iaedb0326db12551dd7a491fe3ab9ee6c53ed5e47 --- Android.bp | 1 + src/com/android/calendar/alerts/AlarmManagerInterface.kt | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Android.bp b/Android.bp index 4d529aee..cae31002 100644 --- a/Android.bp +++ b/Android.bp @@ -17,6 +17,7 @@ license { // Include res dir from chips exclude_srcsd = [ + "src/**/calendar/alerts/AlarmManagerInterface.java", "src/**/calendar/alerts/AlertReceiver.java", "src/**/calendar/alerts/AlertUtils.java", "src/**/calendar/alerts/AlertService.java", diff --git a/src/com/android/calendar/alerts/AlarmManagerInterface.kt b/src/com/android/calendar/alerts/AlarmManagerInterface.kt index 3c66434d..be9d86f2 100644 --- a/src/com/android/calendar/alerts/AlarmManagerInterface.kt +++ b/src/com/android/calendar/alerts/AlarmManagerInterface.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar.alerts -package com.android.calendar.alerts; - -import android.app.PendingIntent; +import android.app.PendingIntent /** * AlarmManager abstracted to an interface for testability. */ -public interface AlarmManagerInterface { - public void set(int type, long triggerAtMillis, PendingIntent operation); -} +interface AlarmManagerInterface { + operator fun set(type: Int, triggerAtMillis: Long, operation: PendingIntent?) +} \ No newline at end of file -- cgit v1.2.3 From b9dc65a640f86475d4f1c5ba7336cda007cc0521 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Mon, 14 Jun 2021 18:32:31 +0000 Subject: AOSP/Calendar - Add Kotlin copy of CalendarUtils.java Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: I308b2a6c1084bcfbe8bb5b391a64d361438fc966 --- src/com/android/calendar/CalendarUtils.kt | 356 ++++++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) create mode 100644 src/com/android/calendar/CalendarUtils.kt diff --git a/src/com/android/calendar/CalendarUtils.kt b/src/com/android/calendar/CalendarUtils.kt new file mode 100644 index 00000000..0238c321 --- /dev/null +++ b/src/com/android/calendar/CalendarUtils.kt @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2010 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.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.os.Looper; +import android.provider.CalendarContract.CalendarCache; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Log; + +import java.util.Formatter; +import java.util.HashSet; +import java.util.Locale; + +/** + * A class containing utility methods related to Calendar apps. + * + * This class is expected to move into the app framework eventually. + */ +public class CalendarUtils { + private static final boolean DEBUG = false; + private static final String TAG = "CalendarUtils"; + + /** + * This class contains methods specific to reading and writing time zone + * values. + */ + public static class TimeZoneUtils { + private static final String[] TIMEZONE_TYPE_ARGS = { CalendarCache.KEY_TIMEZONE_TYPE }; + private static final String[] TIMEZONE_INSTANCES_ARGS = + { CalendarCache.KEY_TIMEZONE_INSTANCES }; + public static final String[] CALENDAR_CACHE_POJECTION = { + CalendarCache.KEY, CalendarCache.VALUE + }; + + private static StringBuilder mSB = new StringBuilder(50); + private static Formatter mF = new Formatter(mSB, Locale.getDefault()); + private volatile static boolean mFirstTZRequest = true; + private volatile static boolean mTZQueryInProgress = false; + + private volatile static boolean mUseHomeTZ = false; + private volatile static String mHomeTZ = Time.getCurrentTimezone(); + + private static HashSet mTZCallbacks = new HashSet(); + private static int mToken = 1; + private static AsyncTZHandler mHandler; + + // The name of the shared preferences file. This name must be maintained for historical + // reasons, as it's what PreferenceManager assigned the first time the file was created. + private final String mPrefsName; + + /** + * This is the key used for writing whether or not a home time zone should + * be used in the Calendar app to the Calendar Preferences. + */ + public static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled"; + /** + * This is the key used for writing the time zone that should be used if + * home time zones are enabled for the Calendar app. + */ + public static final String KEY_HOME_TZ = "preferences_home_tz"; + + /** + * This is a helper class for handling the async queries and updates for the + * time zone settings in Calendar. + */ + private class AsyncTZHandler extends AsyncQueryHandler { + public AsyncTZHandler(ContentResolver cr) { + super(cr); + } + + @Override + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + synchronized (mTZCallbacks) { + if (cursor == null) { + mTZQueryInProgress = false; + mFirstTZRequest = true; + return; + } + + boolean writePrefs = false; + // Check the values in the db + int keyColumn = cursor.getColumnIndexOrThrow(CalendarCache.KEY); + int valueColumn = cursor.getColumnIndexOrThrow(CalendarCache.VALUE); + while(cursor.moveToNext()) { + String key = cursor.getString(keyColumn); + String value = cursor.getString(valueColumn); + if (TextUtils.equals(key, CalendarCache.KEY_TIMEZONE_TYPE)) { + boolean useHomeTZ = !TextUtils.equals( + value, CalendarCache.TIMEZONE_TYPE_AUTO); + if (useHomeTZ != mUseHomeTZ) { + writePrefs = true; + mUseHomeTZ = useHomeTZ; + } + } else if (TextUtils.equals( + key, CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { + if (!TextUtils.isEmpty(value) && !TextUtils.equals(mHomeTZ, value)) { + writePrefs = true; + mHomeTZ = value; + } + } + } + cursor.close(); + if (writePrefs) { + SharedPreferences prefs = getSharedPreferences((Context)cookie, mPrefsName); + // Write the prefs + setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ); + setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ); + } + + mTZQueryInProgress = false; + for (Runnable callback : mTZCallbacks) { + if (callback != null) { + callback.run(); + } + } + mTZCallbacks.clear(); + } + } + } + + /** + * The name of the file where the shared prefs for Calendar are stored + * must be provided. All activities within an app should provide the + * same preferences name or behavior may become erratic. + * + * @param prefsName + */ + public TimeZoneUtils(String prefsName) { + mPrefsName = prefsName; + } + + /** + * Formats a date or a time range according to the local conventions. + * + * This formats a date/time range using Calendar's time zone and the + * local conventions for the region of the device. + * + * If the {@link DateUtils#FORMAT_UTC} flag is used it will pass in + * the UTC time zone instead. + * + * @param context the context is required only if the time is shown + * @param startMillis the start time in UTC milliseconds + * @param endMillis the end time in UTC milliseconds + * @param flags a bit mask of options See + * {@link DateUtils#formatDateRange(Context, Formatter, long, long, int, String) formatDateRange} + * @return a string containing the formatted date/time range. + */ + public String formatDateRange(Context context, long startMillis, + long endMillis, int flags) { + String date; + String tz; + if ((flags & DateUtils.FORMAT_UTC) != 0) { + tz = Time.TIMEZONE_UTC; + } else { + tz = getTimeZone(context, null); + } + synchronized (mSB) { + mSB.setLength(0); + date = DateUtils.formatDateRange(context, mF, startMillis, endMillis, flags, + tz).toString(); + } + return date; + } + + /** + * Writes a new home time zone to the db. + * + * Updates the home time zone in the db asynchronously and updates + * the local cache. Sending a time zone of + * {@link CalendarCache#TIMEZONE_TYPE_AUTO} will cause it to be set + * to the device's time zone. null or empty tz will be ignored. + * + * @param context The calling activity + * @param timeZone The time zone to set Calendar to, or + * {@link CalendarCache#TIMEZONE_TYPE_AUTO} + */ + public void setTimeZone(Context context, String timeZone) { + if (TextUtils.isEmpty(timeZone)) { + if (DEBUG) { + Log.d(TAG, "Empty time zone, nothing to be done."); + } + return; + } + boolean updatePrefs = false; + synchronized (mTZCallbacks) { + if (CalendarCache.TIMEZONE_TYPE_AUTO.equals(timeZone)) { + if (mUseHomeTZ) { + updatePrefs = true; + } + mUseHomeTZ = false; + } else { + if (!mUseHomeTZ || !TextUtils.equals(mHomeTZ, timeZone)) { + updatePrefs = true; + } + mUseHomeTZ = true; + mHomeTZ = timeZone; + } + } + if (updatePrefs) { + // Write the prefs + SharedPreferences prefs = getSharedPreferences(context, mPrefsName); + setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ); + setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ); + + // Update the db + ContentValues values = new ContentValues(); + if (mHandler != null) { + mHandler.cancelOperation(mToken); + } + + mHandler = new AsyncTZHandler(context.getContentResolver()); + + // skip 0 so query can use it + if (++mToken == 0) { + mToken = 1; + } + + // Write the use home tz setting + values.put(CalendarCache.VALUE, mUseHomeTZ ? CalendarCache.TIMEZONE_TYPE_HOME + : CalendarCache.TIMEZONE_TYPE_AUTO); + mHandler.startUpdate(mToken, null, CalendarCache.URI, values, "key=?", + TIMEZONE_TYPE_ARGS); + + // If using a home tz write it to the db + if (mUseHomeTZ) { + ContentValues values2 = new ContentValues(); + values2.put(CalendarCache.VALUE, mHomeTZ); + mHandler.startUpdate(mToken, null, CalendarCache.URI, values2, + "key=?", TIMEZONE_INSTANCES_ARGS); + } + } + } + + /** + * Gets the time zone that Calendar should be displayed in + * + * This is a helper method to get the appropriate time zone for Calendar. If this + * is the first time this method has been called it will initiate an asynchronous + * query to verify that the data in preferences is correct. The callback supplied + * will only be called if this query returns a value other than what is stored in + * preferences and should cause the calling activity to refresh anything that + * depends on calling this method. + * + * @param context The calling activity + * @param callback The runnable that should execute if a query returns new values + * @return The string value representing the time zone Calendar should display + */ + public String getTimeZone(Context context, Runnable callback) { + synchronized (mTZCallbacks){ + if (mFirstTZRequest) { + SharedPreferences prefs = getSharedPreferences(context, mPrefsName); + mUseHomeTZ = prefs.getBoolean(KEY_HOME_TZ_ENABLED, false); + mHomeTZ = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()); + + // Only check content resolver if we have a looper to attach to use + if (Looper.myLooper() != null) { + mTZQueryInProgress = true; + mFirstTZRequest = false; + + // When the async query returns it should synchronize on + // mTZCallbacks, update mUseHomeTZ, mHomeTZ, and the + // preferences, set mTZQueryInProgress to false, and call all + // the runnables in mTZCallbacks. + if (mHandler == null) { + mHandler = new AsyncTZHandler(context.getContentResolver()); + } + mHandler.startQuery(0, context, CalendarCache.URI, CALENDAR_CACHE_POJECTION, + null, null, null); + } + } + if (mTZQueryInProgress) { + mTZCallbacks.add(callback); + } + } + return mUseHomeTZ ? mHomeTZ : Time.getCurrentTimezone(); + } + + /** + * Forces a query of the database to check for changes to the time zone. + * This should be called if another app may have modified the db. If a + * query is already in progress the callback will be added to the list + * of callbacks to be called when it returns. + * + * @param context The calling activity + * @param callback The runnable that should execute if a query returns + * new values + */ + public void forceDBRequery(Context context, Runnable callback) { + synchronized (mTZCallbacks){ + if (mTZQueryInProgress) { + mTZCallbacks.add(callback); + return; + } + mFirstTZRequest = true; + getTimeZone(context, callback); + } + } + } + + /** + * A helper method for writing a String value to the preferences + * asynchronously. + * + * @param context A context with access to the correct preferences + * @param key The preference to write to + * @param value The value to write + */ + public static void setSharedPreference(SharedPreferences prefs, String key, String value) { +// SharedPreferences prefs = getSharedPreferences(context); + SharedPreferences.Editor editor = prefs.edit(); + editor.putString(key, value); + editor.apply(); + } + + /** + * A helper method for writing a boolean value to the preferences + * asynchronously. + * + * @param context A context with access to the correct preferences + * @param key The preference to write to + * @param value The value to write + */ + public static void setSharedPreference(SharedPreferences prefs, String key, boolean value) { +// SharedPreferences prefs = getSharedPreferences(context, prefsName); + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(key, value); + editor.apply(); + } + + /** Return a properly configured SharedPreferences instance */ + public static SharedPreferences getSharedPreferences(Context context, String prefsName) { + return context.getSharedPreferences(prefsName, Context.MODE_PRIVATE); + } +} -- cgit v1.2.3 From 7abfcd30752c0433d9c28c047c14cc4122e8c065 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Mon, 14 Jun 2021 14:41:50 +0000 Subject: AOSP/Calendar - Copy of AlarmScheduler.java .kt file The Java code in AlarmScheduler.java has been copied into a new file named AlarmScheduler.kt. Test: manual - opening both files shows they are identical. Change-Id: I2052e107067f2717d570b7ea37819dd78c594165 --- src/com/android/calendar/alerts/AlarmScheduler.kt | 322 ++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 src/com/android/calendar/alerts/AlarmScheduler.kt diff --git a/src/com/android/calendar/alerts/AlarmScheduler.kt b/src/com/android/calendar/alerts/AlarmScheduler.kt new file mode 100644 index 00000000..97828229 --- /dev/null +++ b/src/com/android/calendar/alerts/AlarmScheduler.kt @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2012 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.alerts; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.provider.CalendarContract; +import android.provider.CalendarContract.Events; +import android.provider.CalendarContract.Instances; +import android.provider.CalendarContract.Reminders; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Log; + +import com.android.calendar.Utils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Schedules the next EVENT_REMINDER_APP broadcast with AlarmManager, by querying the events + * and reminders tables for the next upcoming alert. + */ +public class AlarmScheduler { + private static final String TAG = "AlarmScheduler"; + + private static final String INSTANCES_WHERE = Events.VISIBLE + "=? AND " + + Instances.BEGIN + ">=? AND " + Instances.BEGIN + "<=? AND " + + Events.ALL_DAY + "=?"; + static final String[] INSTANCES_PROJECTION = new String[] { + Instances.EVENT_ID, + Instances.BEGIN, + Instances.ALL_DAY, + }; + private static final int INSTANCES_INDEX_EVENTID = 0; + private static final int INSTANCES_INDEX_BEGIN = 1; + private static final int INSTANCES_INDEX_ALL_DAY = 2; + + private static final String REMINDERS_WHERE = Reminders.METHOD + "=1 AND " + + Reminders.EVENT_ID + " IN "; + static final String[] REMINDERS_PROJECTION = new String[] { + Reminders.EVENT_ID, + Reminders.MINUTES, + Reminders.METHOD, + }; + private static final int REMINDERS_INDEX_EVENT_ID = 0; + private static final int REMINDERS_INDEX_MINUTES = 1; + private static final int REMINDERS_INDEX_METHOD = 2; + + // Add a slight delay for the EVENT_REMINDER_APP broadcast for a couple reasons: + // (1) so that the concurrent reminder broadcast from the provider doesn't result + // in a double ring, and (2) some OEMs modified the provider to not add an alert to + // the CalendarAlerts table until the alert time, so for the unbundled app's + // notifications to work on these devices, a delay ensures that AlertService won't + // read from the CalendarAlerts table until the alert is present. + static final int ALARM_DELAY_MS = 1000; + + // The reminders query looks like "SELECT ... AND eventId IN 101,102,202,...". This + // sets the max # of events in the query before batching into multiple queries, to + // limit the SQL query length. + private static final int REMINDER_QUERY_BATCH_SIZE = 50; + + // We really need to query for reminder times that fall in some interval, but + // the Reminders table only stores the reminder interval (10min, 15min, etc), and + // we cannot do the join with the Events table to calculate the actual alert time + // from outside of the provider. So the best we can do for now consider events + // whose start times begin within some interval (ie. 1 week out). This means + // reminders which are configured for more than 1 week out won't fire on time. We + // can minimize this to being only 1 day late by putting a 1 day max on the alarm time. + private static final long EVENT_LOOKAHEAD_WINDOW_MS = DateUtils.WEEK_IN_MILLIS; + private static final long MAX_ALARM_ELAPSED_MS = DateUtils.DAY_IN_MILLIS; + + /** + * Schedules the nearest upcoming alarm, to refresh notifications. + * + * This is historically done in the provider but we dupe this here so the unbundled + * app will work on devices that have modified this portion of the provider. This + * has the limitation of querying events within some interval from now (ie. looks at + * reminders for all events occurring in the next week). This means for example, + * a 2 week notification will not fire on time. + */ + public static void scheduleNextAlarm(Context context) { + scheduleNextAlarm(context, AlertUtils.createAlarmManager(context), + REMINDER_QUERY_BATCH_SIZE, System.currentTimeMillis()); + } + + // VisibleForTesting + static void scheduleNextAlarm(Context context, AlarmManagerInterface alarmManager, + int batchSize, long currentMillis) { + Cursor instancesCursor = null; + try { + instancesCursor = queryUpcomingEvents(context, context.getContentResolver(), + currentMillis); + if (instancesCursor != null) { + queryNextReminderAndSchedule(instancesCursor, context, + context.getContentResolver(), alarmManager, batchSize, currentMillis); + } + } finally { + if (instancesCursor != null) { + instancesCursor.close(); + } + } + } + + /** + * Queries events starting within a fixed interval from now. + */ + private static Cursor queryUpcomingEvents(Context context, ContentResolver contentResolver, + long currentMillis) { + Time time = new Time(); + time.normalize(false); + long localOffset = time.gmtoff * 1000; + final long localStartMin = currentMillis; + final long localStartMax = localStartMin + EVENT_LOOKAHEAD_WINDOW_MS; + final long utcStartMin = localStartMin - localOffset; + final long utcStartMax = utcStartMin + EVENT_LOOKAHEAD_WINDOW_MS; + + // Expand Instances table range by a day on either end to account for + // all-day events. + Uri.Builder uriBuilder = Instances.CONTENT_URI.buildUpon(); + ContentUris.appendId(uriBuilder, localStartMin - DateUtils.DAY_IN_MILLIS); + ContentUris.appendId(uriBuilder, localStartMax + DateUtils.DAY_IN_MILLIS); + + // Build query for all events starting within the fixed interval. + StringBuilder queryBuilder = new StringBuilder(); + queryBuilder.append("("); + queryBuilder.append(INSTANCES_WHERE); + queryBuilder.append(") OR ("); + queryBuilder.append(INSTANCES_WHERE); + queryBuilder.append(")"); + String[] queryArgs = new String[] { + // allday selection + "1", /* visible = ? */ + String.valueOf(utcStartMin), /* begin >= ? */ + String.valueOf(utcStartMax), /* begin <= ? */ + "1", /* allDay = ? */ + + // non-allday selection + "1", /* visible = ? */ + String.valueOf(localStartMin), /* begin >= ? */ + String.valueOf(localStartMax), /* begin <= ? */ + "0" /* allDay = ? */ + }; + + Cursor cursor = contentResolver.query(uriBuilder.build(), INSTANCES_PROJECTION, + queryBuilder.toString(), queryArgs, null); + return cursor; + } + + /** + * Queries for all the reminders of the events in the instancesCursor, and schedules + * the alarm for the next upcoming reminder. + */ + private static void queryNextReminderAndSchedule(Cursor instancesCursor, Context context, + ContentResolver contentResolver, AlarmManagerInterface alarmManager, + int batchSize, long currentMillis) { + if (AlertService.DEBUG) { + int eventCount = instancesCursor.getCount(); + if (eventCount == 0) { + Log.d(TAG, "No events found starting within 1 week."); + } else { + Log.d(TAG, "Query result count for events starting within 1 week: " + eventCount); + } + } + + // Put query results of all events starting within some interval into map of event ID to + // local start time. + Map> eventMap = new HashMap>(); + Time timeObj = new Time(); + long nextAlarmTime = Long.MAX_VALUE; + int nextAlarmEventId = 0; + instancesCursor.moveToPosition(-1); + while (!instancesCursor.isAfterLast()) { + int index = 0; + eventMap.clear(); + StringBuilder eventIdsForQuery = new StringBuilder(); + eventIdsForQuery.append('('); + while (index++ < batchSize && instancesCursor.moveToNext()) { + int eventId = instancesCursor.getInt(INSTANCES_INDEX_EVENTID); + long begin = instancesCursor.getLong(INSTANCES_INDEX_BEGIN); + boolean allday = instancesCursor.getInt(INSTANCES_INDEX_ALL_DAY) != 0; + long localStartTime; + if (allday) { + // Adjust allday to local time. + localStartTime = Utils.convertAlldayUtcToLocal(timeObj, begin, + Time.getCurrentTimezone()); + } else { + localStartTime = begin; + } + List startTimes = eventMap.get(eventId); + if (startTimes == null) { + startTimes = new ArrayList(); + eventMap.put(eventId, startTimes); + eventIdsForQuery.append(eventId); + eventIdsForQuery.append(","); + } + startTimes.add(localStartTime); + + // Log for debugging. + if (Log.isLoggable(TAG, Log.DEBUG)) { + timeObj.set(localStartTime); + StringBuilder msg = new StringBuilder(); + msg.append("Events cursor result -- eventId:").append(eventId); + msg.append(", allDay:").append(allday); + msg.append(", start:").append(localStartTime); + msg.append(" (").append(timeObj.format("%a, %b %d, %Y %I:%M%P")).append(")"); + Log.d(TAG, msg.toString()); + } + } + if (eventIdsForQuery.charAt(eventIdsForQuery.length() - 1) == ',') { + eventIdsForQuery.deleteCharAt(eventIdsForQuery.length() - 1); + } + eventIdsForQuery.append(')'); + + // Query the reminders table for the events found. + Cursor cursor = null; + try { + cursor = contentResolver.query(Reminders.CONTENT_URI, REMINDERS_PROJECTION, + REMINDERS_WHERE + eventIdsForQuery, null, null); + + // Process the reminders query results to find the next reminder time. + cursor.moveToPosition(-1); + while (cursor.moveToNext()) { + int eventId = cursor.getInt(REMINDERS_INDEX_EVENT_ID); + int reminderMinutes = cursor.getInt(REMINDERS_INDEX_MINUTES); + List startTimes = eventMap.get(eventId); + if (startTimes != null) { + for (Long startTime : startTimes) { + long alarmTime = startTime - + reminderMinutes * DateUtils.MINUTE_IN_MILLIS; + if (alarmTime > currentMillis && alarmTime < nextAlarmTime) { + nextAlarmTime = alarmTime; + nextAlarmEventId = eventId; + } + + if (Log.isLoggable(TAG, Log.DEBUG)) { + timeObj.set(alarmTime); + StringBuilder msg = new StringBuilder(); + msg.append("Reminders cursor result -- eventId:").append(eventId); + msg.append(", startTime:").append(startTime); + msg.append(", minutes:").append(reminderMinutes); + msg.append(", alarmTime:").append(alarmTime); + msg.append(" (").append(timeObj.format("%a, %b %d, %Y %I:%M%P")) + .append(")"); + Log.d(TAG, msg.toString()); + } + } + } + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + } + + // Schedule the alarm for the next reminder time. + if (nextAlarmTime < Long.MAX_VALUE) { + scheduleAlarm(context, nextAlarmEventId, nextAlarmTime, currentMillis, alarmManager); + } + } + + /** + * Schedules an alarm for the EVENT_REMINDER_APP broadcast, for the specified + * alarm time with a slight delay (to account for the possible duplicate broadcast + * from the provider). + */ + private static void scheduleAlarm(Context context, long eventId, long alarmTime, + long currentMillis, AlarmManagerInterface alarmManager) { + // Max out the alarm time to 1 day out, so an alert for an event far in the future + // (not present in our event query results for a limited range) can only be at + // most 1 day late. + long maxAlarmTime = currentMillis + MAX_ALARM_ELAPSED_MS; + if (alarmTime > maxAlarmTime) { + alarmTime = maxAlarmTime; + } + + // Add a slight delay (see comments on the member var). + alarmTime += ALARM_DELAY_MS; + + if (AlertService.DEBUG) { + Time time = new Time(); + time.set(alarmTime); + String schedTime = time.format("%a, %b %d, %Y %I:%M%P"); + Log.d(TAG, "Scheduling alarm for EVENT_REMINDER_APP broadcast for event " + eventId + + " at " + alarmTime + " (" + schedTime + ")"); + } + + // Schedule an EVENT_REMINDER_APP broadcast with AlarmManager. The extra is + // only used by AlertService for logging. It is ignored by Intent.filterEquals, + // so this scheduling will still overwrite the alarm that was previously pending. + // Note that the 'setClass' is required, because otherwise it seems the broadcast + // can be eaten by other apps and we somehow may never receive it. + Intent intent = new Intent(AlertReceiver.EVENT_REMINDER_APP_ACTION); + intent.setClass(context, AlertReceiver.class); + intent.putExtra(CalendarContract.CalendarAlerts.ALARM_TIME, alarmTime); + PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0); + alarmManager.set(AlarmManager.RTC_WAKEUP, alarmTime, pi); + } +} -- cgit v1.2.3 From bfa4de2f781f3ea228c3eaaf0d738b4570ffcacd Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Mon, 14 Jun 2021 22:36:13 +0000 Subject: AOSP/Calendar - AlarmScheduler.java fully converted with bp file Using Android Studio, AlarmScheduler.java has been converted to Kotlin. The automatic converter did not do a great job of accounting for Kotlin's unique nullability and type inference features particularly with Lists, ArrayLists, and HashMaps whose variables had to be manually corrected in order to maintain consistency. Moreover, since essentially all members of this class are static, the converter declared AlarmScheduler as an object rather than a class. In essence, this allows Kotlin functions and properties to be treated as if they were static members in a Java class. However, the converter does not add the @JvmStatic annotation for functions which enforces that they behave like static methods. This had to be added manually. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.159 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 6m 19s Total aggregated tests run time: 6m 19s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.99 tests/sec [376 tests / 379536 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 7138 ms || clean = 2025 ms Total preparation time: 7s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 7m 53s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: Ie39162b7135bb065820647403d7e7c4051b73507 --- Android.bp | 3 +- src/com/android/calendar/alerts/AlarmScheduler.kt | 380 ++++++++++++---------- 2 files changed, 207 insertions(+), 176 deletions(-) diff --git a/Android.bp b/Android.bp index cae31002..7f45042c 100644 --- a/Android.bp +++ b/Android.bp @@ -18,9 +18,10 @@ license { // Include res dir from chips exclude_srcsd = [ "src/**/calendar/alerts/AlarmManagerInterface.java", + "src/**/calendar/alerts/AlarmScheduler.java", "src/**/calendar/alerts/AlertReceiver.java", - "src/**/calendar/alerts/AlertUtils.java", "src/**/calendar/alerts/AlertService.java", + "src/**/calendar/alerts/AlertUtils.java", "src/**/calendar/DayOfMonthDrawable.java", ] diff --git a/src/com/android/calendar/alerts/AlarmScheduler.kt b/src/com/android/calendar/alerts/AlarmScheduler.kt index 97828229..c93bbb04 100644 --- a/src/com/android/calendar/alerts/AlarmScheduler.kt +++ b/src/com/android/calendar/alerts/AlarmScheduler.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,61 +13,54 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar.alerts -package com.android.calendar.alerts; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.provider.CalendarContract; -import android.provider.CalendarContract.Events; -import android.provider.CalendarContract.Instances; -import android.provider.CalendarContract.Reminders; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; - -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.ContentResolver +import android.content.ContentUris +import android.content.Context +import android.content.Intent +import android.database.Cursor +import android.net.Uri +import android.provider.CalendarContract +import android.provider.CalendarContract.Events +import android.provider.CalendarContract.Instances +import android.provider.CalendarContract.Reminders +import android.text.format.DateUtils +import android.text.format.Time +import android.util.Log +import com.android.calendar.Utils +import java.util.HashMap +import java.util.List /** * Schedules the next EVENT_REMINDER_APP broadcast with AlarmManager, by querying the events * and reminders tables for the next upcoming alert. */ -public class AlarmScheduler { - private static final String TAG = "AlarmScheduler"; - - private static final String INSTANCES_WHERE = Events.VISIBLE + "=? AND " - + Instances.BEGIN + ">=? AND " + Instances.BEGIN + "<=? AND " - + Events.ALL_DAY + "=?"; - static final String[] INSTANCES_PROJECTION = new String[] { +object AlarmScheduler { + private const val TAG = "AlarmScheduler" + private val INSTANCES_WHERE: String = (Events.VISIBLE.toString() + "=? AND " + + Instances.BEGIN + ">=? AND " + Instances.BEGIN + "<=? AND " + + Events.ALL_DAY + "=?") + val INSTANCES_PROJECTION = arrayOf( Instances.EVENT_ID, Instances.BEGIN, - Instances.ALL_DAY, - }; - private static final int INSTANCES_INDEX_EVENTID = 0; - private static final int INSTANCES_INDEX_BEGIN = 1; - private static final int INSTANCES_INDEX_ALL_DAY = 2; - - private static final String REMINDERS_WHERE = Reminders.METHOD + "=1 AND " - + Reminders.EVENT_ID + " IN "; - static final String[] REMINDERS_PROJECTION = new String[] { + Instances.ALL_DAY + ) + private const val INSTANCES_INDEX_EVENTID = 0 + private const val INSTANCES_INDEX_BEGIN = 1 + private const val INSTANCES_INDEX_ALL_DAY = 2 + private val REMINDERS_WHERE: String = (Reminders.METHOD.toString() + "=1 AND " + + Reminders.EVENT_ID + " IN ") + val REMINDERS_PROJECTION = arrayOf( Reminders.EVENT_ID, Reminders.MINUTES, - Reminders.METHOD, - }; - private static final int REMINDERS_INDEX_EVENT_ID = 0; - private static final int REMINDERS_INDEX_MINUTES = 1; - private static final int REMINDERS_INDEX_METHOD = 2; + Reminders.METHOD + ) + private const val REMINDERS_INDEX_EVENT_ID = 0 + private const val REMINDERS_INDEX_MINUTES = 1 + private const val REMINDERS_INDEX_METHOD = 2 // Add a slight delay for the EVENT_REMINDER_APP broadcast for a couple reasons: // (1) so that the concurrent reminder broadcast from the provider doesn't result @@ -75,12 +68,12 @@ public class AlarmScheduler { // the CalendarAlerts table until the alert time, so for the unbundled app's // notifications to work on these devices, a delay ensures that AlertService won't // read from the CalendarAlerts table until the alert is present. - static final int ALARM_DELAY_MS = 1000; + const val ALARM_DELAY_MS = 1000 // The reminders query looks like "SELECT ... AND eventId IN 101,102,202,...". This // sets the max # of events in the query before batching into multiple queries, to // limit the SQL query length. - private static final int REMINDER_QUERY_BATCH_SIZE = 50; + private const val REMINDER_QUERY_BATCH_SIZE = 50 // We really need to query for reminder times that fall in some interval, but // the Reminders table only stores the reminder interval (10min, 15min, etc), and @@ -89,8 +82,8 @@ public class AlarmScheduler { // whose start times begin within some interval (ie. 1 week out). This means // reminders which are configured for more than 1 week out won't fire on time. We // can minimize this to being only 1 day late by putting a 1 day max on the alarm time. - private static final long EVENT_LOOKAHEAD_WINDOW_MS = DateUtils.WEEK_IN_MILLIS; - private static final long MAX_ALARM_ELAPSED_MS = DateUtils.DAY_IN_MILLIS; + private val EVENT_LOOKAHEAD_WINDOW_MS: Long = DateUtils.WEEK_IN_MILLIS + private val MAX_ALARM_ELAPSED_MS: Long = DateUtils.DAY_IN_MILLIS /** * Schedules the nearest upcoming alarm, to refresh notifications. @@ -101,25 +94,39 @@ public class AlarmScheduler { * reminders for all events occurring in the next week). This means for example, * a 2 week notification will not fire on time. */ - public static void scheduleNextAlarm(Context context) { - scheduleNextAlarm(context, AlertUtils.createAlarmManager(context), - REMINDER_QUERY_BATCH_SIZE, System.currentTimeMillis()); + @JvmStatic fun scheduleNextAlarm(context: Context) { + scheduleNextAlarm( + context, AlertUtils.createAlarmManager(context), + REMINDER_QUERY_BATCH_SIZE, System.currentTimeMillis() + ) } // VisibleForTesting - static void scheduleNextAlarm(Context context, AlarmManagerInterface alarmManager, - int batchSize, long currentMillis) { - Cursor instancesCursor = null; + @JvmStatic fun scheduleNextAlarm( + context: Context, + alarmManager: AlarmManagerInterface?, + batchSize: Int, + currentMillis: Long + ) { + var instancesCursor: Cursor? = null try { - instancesCursor = queryUpcomingEvents(context, context.getContentResolver(), - currentMillis); + instancesCursor = queryUpcomingEvents( + context, context.getContentResolver(), + currentMillis + ) if (instancesCursor != null) { - queryNextReminderAndSchedule(instancesCursor, context, - context.getContentResolver(), alarmManager, batchSize, currentMillis); + queryNextReminderAndSchedule( + instancesCursor, + context, + context.getContentResolver(), + alarmManager as AlarmManagerInterface, + batchSize, + currentMillis + ) } } finally { if (instancesCursor != null) { - instancesCursor.close(); + instancesCursor.close() } } } @@ -127,158 +134,174 @@ public class AlarmScheduler { /** * Queries events starting within a fixed interval from now. */ - private static Cursor queryUpcomingEvents(Context context, ContentResolver contentResolver, - long currentMillis) { - Time time = new Time(); - time.normalize(false); - long localOffset = time.gmtoff * 1000; - final long localStartMin = currentMillis; - final long localStartMax = localStartMin + EVENT_LOOKAHEAD_WINDOW_MS; - final long utcStartMin = localStartMin - localOffset; - final long utcStartMax = utcStartMin + EVENT_LOOKAHEAD_WINDOW_MS; + @JvmStatic private fun queryUpcomingEvents( + context: Context, + contentResolver: ContentResolver, + currentMillis: Long + ): Cursor? { + val time = Time() + time.normalize(false) + val localOffset: Long = time.gmtoff * 1000 + val localStartMax = + currentMillis + EVENT_LOOKAHEAD_WINDOW_MS + val utcStartMin = currentMillis - localOffset + val utcStartMax = + utcStartMin + EVENT_LOOKAHEAD_WINDOW_MS // Expand Instances table range by a day on either end to account for // all-day events. - Uri.Builder uriBuilder = Instances.CONTENT_URI.buildUpon(); - ContentUris.appendId(uriBuilder, localStartMin - DateUtils.DAY_IN_MILLIS); - ContentUris.appendId(uriBuilder, localStartMax + DateUtils.DAY_IN_MILLIS); + val uriBuilder: Uri.Builder = Instances.CONTENT_URI.buildUpon() + ContentUris.appendId(uriBuilder, currentMillis - DateUtils.DAY_IN_MILLIS) + ContentUris.appendId(uriBuilder, localStartMax + DateUtils.DAY_IN_MILLIS) // Build query for all events starting within the fixed interval. - StringBuilder queryBuilder = new StringBuilder(); - queryBuilder.append("("); - queryBuilder.append(INSTANCES_WHERE); - queryBuilder.append(") OR ("); - queryBuilder.append(INSTANCES_WHERE); - queryBuilder.append(")"); - String[] queryArgs = new String[] { - // allday selection - "1", /* visible = ? */ - String.valueOf(utcStartMin), /* begin >= ? */ - String.valueOf(utcStartMax), /* begin <= ? */ - "1", /* allDay = ? */ - - // non-allday selection - "1", /* visible = ? */ - String.valueOf(localStartMin), /* begin >= ? */ - String.valueOf(localStartMax), /* begin <= ? */ - "0" /* allDay = ? */ - }; + val queryBuilder = StringBuilder() + queryBuilder.append("(") + queryBuilder.append(INSTANCES_WHERE) + queryBuilder.append(") OR (") + queryBuilder.append(INSTANCES_WHERE) + queryBuilder.append(")") + val queryArgs = arrayOf( + // allday selection + "1", /* visible = ? */ + utcStartMin.toString(), /* begin >= ? */ + utcStartMax.toString(), /* begin <= ? */ + "1", /* allDay = ? */ // non-allday selection + "1", /* visible = ? */ + currentMillis.toString(), /* begin >= ? */ + localStartMax.toString(), /* begin <= ? */ + "0" /* allDay = ? */ + ) - Cursor cursor = contentResolver.query(uriBuilder.build(), INSTANCES_PROJECTION, - queryBuilder.toString(), queryArgs, null); - return cursor; + val cursor: Cursor? = contentResolver.query(uriBuilder.build(), INSTANCES_PROJECTION, + queryBuilder.toString(), queryArgs, null) + return cursor } /** * Queries for all the reminders of the events in the instancesCursor, and schedules * the alarm for the next upcoming reminder. */ - private static void queryNextReminderAndSchedule(Cursor instancesCursor, Context context, - ContentResolver contentResolver, AlarmManagerInterface alarmManager, - int batchSize, long currentMillis) { + @JvmStatic private fun queryNextReminderAndSchedule( + instancesCursor: Cursor, + context: Context, + contentResolver: ContentResolver, + alarmManager: AlarmManagerInterface, + batchSize: Int, + currentMillis: Long + ) { if (AlertService.DEBUG) { - int eventCount = instancesCursor.getCount(); + val eventCount: Int = instancesCursor.getCount() if (eventCount == 0) { - Log.d(TAG, "No events found starting within 1 week."); + Log.d(TAG, "No events found starting within 1 week.") } else { - Log.d(TAG, "Query result count for events starting within 1 week: " + eventCount); + Log.d(TAG, "Query result count for events starting within 1 week: $eventCount") } } // Put query results of all events starting within some interval into map of event ID to // local start time. - Map> eventMap = new HashMap>(); - Time timeObj = new Time(); - long nextAlarmTime = Long.MAX_VALUE; - int nextAlarmEventId = 0; - instancesCursor.moveToPosition(-1); + val eventMap: HashMap?> = HashMap?>() + val timeObj = Time() + var nextAlarmTime = Long.MAX_VALUE + var nextAlarmEventId = 0 + instancesCursor.moveToPosition(-1) while (!instancesCursor.isAfterLast()) { - int index = 0; - eventMap.clear(); - StringBuilder eventIdsForQuery = new StringBuilder(); - eventIdsForQuery.append('('); + var index = 0 + eventMap.clear() + val eventIdsForQuery = StringBuilder() + eventIdsForQuery.append('(') while (index++ < batchSize && instancesCursor.moveToNext()) { - int eventId = instancesCursor.getInt(INSTANCES_INDEX_EVENTID); - long begin = instancesCursor.getLong(INSTANCES_INDEX_BEGIN); - boolean allday = instancesCursor.getInt(INSTANCES_INDEX_ALL_DAY) != 0; - long localStartTime; - if (allday) { + val eventId: Int = instancesCursor.getInt(INSTANCES_INDEX_EVENTID) + val begin: Long = instancesCursor.getLong(INSTANCES_INDEX_BEGIN) + val allday = instancesCursor.getInt(INSTANCES_INDEX_ALL_DAY) != 0 + var localStartTime: Long + localStartTime = if (allday) { // Adjust allday to local time. - localStartTime = Utils.convertAlldayUtcToLocal(timeObj, begin, - Time.getCurrentTimezone()); + Utils.convertAlldayUtcToLocal( + timeObj, begin, + Time.getCurrentTimezone() + ) } else { - localStartTime = begin; + begin } - List startTimes = eventMap.get(eventId); + var startTimes: List? = eventMap.get(eventId) if (startTimes == null) { - startTimes = new ArrayList(); - eventMap.put(eventId, startTimes); - eventIdsForQuery.append(eventId); - eventIdsForQuery.append(","); + startTimes = mutableListOf() as List + eventMap.put(eventId, startTimes) + eventIdsForQuery.append(eventId) + eventIdsForQuery.append(",") } - startTimes.add(localStartTime); + startTimes.add(localStartTime) // Log for debugging. if (Log.isLoggable(TAG, Log.DEBUG)) { - timeObj.set(localStartTime); - StringBuilder msg = new StringBuilder(); - msg.append("Events cursor result -- eventId:").append(eventId); - msg.append(", allDay:").append(allday); - msg.append(", start:").append(localStartTime); - msg.append(" (").append(timeObj.format("%a, %b %d, %Y %I:%M%P")).append(")"); - Log.d(TAG, msg.toString()); + timeObj.set(localStartTime) + val msg = StringBuilder() + msg.append("Events cursor result -- eventId:").append(eventId) + msg.append(", allDay:").append(allday) + msg.append(", start:").append(localStartTime) + msg.append(" (").append(timeObj.format("%a, %b %d, %Y %I:%M%P")).append(")") + Log.d(TAG, msg.toString()) } } - if (eventIdsForQuery.charAt(eventIdsForQuery.length() - 1) == ',') { - eventIdsForQuery.deleteCharAt(eventIdsForQuery.length() - 1); + if (eventIdsForQuery[eventIdsForQuery.length - 1] == ',') { + eventIdsForQuery.deleteCharAt(eventIdsForQuery.length - 1) } - eventIdsForQuery.append(')'); + eventIdsForQuery.append(')') // Query the reminders table for the events found. - Cursor cursor = null; + var cursor: Cursor? = null try { - cursor = contentResolver.query(Reminders.CONTENT_URI, REMINDERS_PROJECTION, - REMINDERS_WHERE + eventIdsForQuery, null, null); + cursor = contentResolver.query( + Reminders.CONTENT_URI, REMINDERS_PROJECTION, + REMINDERS_WHERE + eventIdsForQuery, null, null + ) // Process the reminders query results to find the next reminder time. - cursor.moveToPosition(-1); - while (cursor.moveToNext()) { - int eventId = cursor.getInt(REMINDERS_INDEX_EVENT_ID); - int reminderMinutes = cursor.getInt(REMINDERS_INDEX_MINUTES); - List startTimes = eventMap.get(eventId); + cursor?.moveToPosition(-1) + while (cursor!!.moveToNext()) { + val eventId: Int = cursor.getInt(REMINDERS_INDEX_EVENT_ID) + val reminderMinutes: Int = cursor.getInt(REMINDERS_INDEX_MINUTES) + val startTimes: List? = eventMap.get(eventId) if (startTimes != null) { - for (Long startTime : startTimes) { - long alarmTime = startTime - - reminderMinutes * DateUtils.MINUTE_IN_MILLIS; + for (startTime in startTimes) { + val alarmTime: Long = startTime - + reminderMinutes * DateUtils.MINUTE_IN_MILLIS if (alarmTime > currentMillis && alarmTime < nextAlarmTime) { - nextAlarmTime = alarmTime; - nextAlarmEventId = eventId; + nextAlarmTime = alarmTime + nextAlarmEventId = eventId } - if (Log.isLoggable(TAG, Log.DEBUG)) { - timeObj.set(alarmTime); - StringBuilder msg = new StringBuilder(); - msg.append("Reminders cursor result -- eventId:").append(eventId); - msg.append(", startTime:").append(startTime); - msg.append(", minutes:").append(reminderMinutes); - msg.append(", alarmTime:").append(alarmTime); + timeObj.set(alarmTime) + val msg = StringBuilder() + msg.append("Reminders cursor result -- eventId:").append(eventId) + msg.append(", startTime:").append(startTime) + msg.append(", minutes:").append(reminderMinutes) + msg.append(", alarmTime:").append(alarmTime) msg.append(" (").append(timeObj.format("%a, %b %d, %Y %I:%M%P")) - .append(")"); - Log.d(TAG, msg.toString()); + .append(")") + Log.d(TAG, msg.toString()) } } } } } finally { if (cursor != null) { - cursor.close(); + cursor.close() } } } // Schedule the alarm for the next reminder time. if (nextAlarmTime < Long.MAX_VALUE) { - scheduleAlarm(context, nextAlarmEventId, nextAlarmTime, currentMillis, alarmManager); + scheduleAlarm( + context, + nextAlarmEventId.toLong(), + nextAlarmTime, + currentMillis, + alarmManager + ) } } @@ -287,25 +310,32 @@ public class AlarmScheduler { * alarm time with a slight delay (to account for the possible duplicate broadcast * from the provider). */ - private static void scheduleAlarm(Context context, long eventId, long alarmTime, - long currentMillis, AlarmManagerInterface alarmManager) { + @JvmStatic private fun scheduleAlarm( + context: Context, + eventId: Long, + alarmTimeInput: Long, + currentMillis: Long, + alarmManager: AlarmManagerInterface + ) { // Max out the alarm time to 1 day out, so an alert for an event far in the future // (not present in our event query results for a limited range) can only be at // most 1 day late. - long maxAlarmTime = currentMillis + MAX_ALARM_ELAPSED_MS; + var alarmTime = alarmTimeInput + val maxAlarmTime = currentMillis + MAX_ALARM_ELAPSED_MS if (alarmTime > maxAlarmTime) { - alarmTime = maxAlarmTime; + alarmTime = maxAlarmTime } // Add a slight delay (see comments on the member var). - alarmTime += ALARM_DELAY_MS; - + alarmTime += ALARM_DELAY_MS.toLong() if (AlertService.DEBUG) { - Time time = new Time(); - time.set(alarmTime); - String schedTime = time.format("%a, %b %d, %Y %I:%M%P"); - Log.d(TAG, "Scheduling alarm for EVENT_REMINDER_APP broadcast for event " + eventId - + " at " + alarmTime + " (" + schedTime + ")"); + val time = Time() + time.set(alarmTime) + val schedTime: String = time.format("%a, %b %d, %Y %I:%M%P") + Log.d( + TAG, "Scheduling alarm for EVENT_REMINDER_APP broadcast for event " + eventId + + " at " + alarmTime + " (" + schedTime + ")" + ) } // Schedule an EVENT_REMINDER_APP broadcast with AlarmManager. The extra is @@ -313,10 +343,10 @@ public class AlarmScheduler { // so this scheduling will still overwrite the alarm that was previously pending. // Note that the 'setClass' is required, because otherwise it seems the broadcast // can be eaten by other apps and we somehow may never receive it. - Intent intent = new Intent(AlertReceiver.EVENT_REMINDER_APP_ACTION); - intent.setClass(context, AlertReceiver.class); - intent.putExtra(CalendarContract.CalendarAlerts.ALARM_TIME, alarmTime); - PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0); - alarmManager.set(AlarmManager.RTC_WAKEUP, alarmTime, pi); + val intent = Intent(AlertReceiver.EVENT_REMINDER_APP_ACTION) + intent.setClass(context, AlertReceiver::class.java) + intent.putExtra(CalendarContract.CalendarAlerts.ALARM_TIME, alarmTime) + val pi: PendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0) + alarmManager.set(AlarmManager.RTC_WAKEUP, alarmTime, pi) } -} +} \ No newline at end of file -- cgit v1.2.3 From 805df83af6f2191a63a27e4d571a951e6c16b0aa Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Tue, 15 Jun 2021 00:14:10 +0000 Subject: AOSP/Calendar - Add Kotlin code for CalendarUtils.kt. Also uploaded corresponding Android.bp file Test: manual (ran build and unit tests) and CTS testing suite $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.166 OK (22 tests) CTS TESTING: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ============================================ ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 11m 6s Total aggregated tests run time: 11m 6s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.56 tests/sec [376 tests / 666667 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 12540 ms || clean = 2632 ms Total preparation time: 12s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 20m 1s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: I919d14f99c60346fa74e599e6d80dc614bc34ee5 --- Android.bp | 1 + src/com/android/calendar/CalendarUtils.kt | 384 +++++++++++++++--------------- 2 files changed, 192 insertions(+), 193 deletions(-) diff --git a/Android.bp b/Android.bp index cae31002..30e20272 100644 --- a/Android.bp +++ b/Android.bp @@ -28,6 +28,7 @@ exclude_srcsm = [ "src/**/calendar/AsyncQueryServiceHelper.java", "src/**/calendar/CalendarApplication.java", "src/**/calendar/CalendarBackupAgent.java", + "src/**/calendar/CalendarUtils.java", "src/**/calendar/EventGeometry.java", "src/**/calendar/GoogleCalendarUriIntentFilter.java", "src/**/calendar/MultiStateButton.java", diff --git a/src/com/android/calendar/CalendarUtils.kt b/src/com/android/calendar/CalendarUtils.kt index 0238c321..94ca7234 100644 --- a/src/com/android/calendar/CalendarUtils.kt +++ b/src/com/android/calendar/CalendarUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,175 +13,171 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar -package com.android.calendar; +import android.content.AsyncQueryHandler +import android.content.ContentResolver +import android.content.ContentValues +import android.content.Context +import android.content.SharedPreferences +import android.database.Cursor +import android.os.Looper +import android.provider.CalendarContract.CalendarCache +import android.text.TextUtils +import android.text.format.DateUtils +import android.text.format.Time +import android.util.Log -import android.content.AsyncQueryHandler; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.os.Looper; -import android.provider.CalendarContract.CalendarCache; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; - -import java.util.Formatter; -import java.util.HashSet; -import java.util.Locale; +import java.util.Formatter +import java.util.HashSet +import java.util.Locale /** * A class containing utility methods related to Calendar apps. * * This class is expected to move into the app framework eventually. */ -public class CalendarUtils { - private static final boolean DEBUG = false; - private static final String TAG = "CalendarUtils"; - - /** - * This class contains methods specific to reading and writing time zone - * values. - */ - public static class TimeZoneUtils { - private static final String[] TIMEZONE_TYPE_ARGS = { CalendarCache.KEY_TIMEZONE_TYPE }; - private static final String[] TIMEZONE_INSTANCES_ARGS = - { CalendarCache.KEY_TIMEZONE_INSTANCES }; - public static final String[] CALENDAR_CACHE_POJECTION = { - CalendarCache.KEY, CalendarCache.VALUE - }; - - private static StringBuilder mSB = new StringBuilder(50); - private static Formatter mF = new Formatter(mSB, Locale.getDefault()); - private volatile static boolean mFirstTZRequest = true; - private volatile static boolean mTZQueryInProgress = false; - - private volatile static boolean mUseHomeTZ = false; - private volatile static String mHomeTZ = Time.getCurrentTimezone(); - - private static HashSet mTZCallbacks = new HashSet(); - private static int mToken = 1; - private static AsyncTZHandler mHandler; +class CalendarUtils { - // The name of the shared preferences file. This name must be maintained for historical - // reasons, as it's what PreferenceManager assigned the first time the file was created. - private final String mPrefsName; + companion object { + private const val DEBUG = false + private const val TAG = "CalendarUtils" /** - * This is the key used for writing whether or not a home time zone should - * be used in the Calendar app to the Calendar Preferences. + * A helper method for writing a boolean value to the preferences + * asynchronously. + * + * @param context A context with access to the correct preferences + * @param key The preference to write to + * @param value The value to write */ - public static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled"; + @JvmStatic + fun setSharedPreference(prefs: SharedPreferences, key: String?, value: Boolean) { + val editor: SharedPreferences.Editor = prefs.edit() + editor.putBoolean(key, value) + editor.apply() + } + + /** Return a properly configured SharedPreferences instance */ + @JvmStatic + fun getSharedPreferences(context: Context, prefsName: String?): SharedPreferences { + return context.getSharedPreferences(prefsName, Context.MODE_PRIVATE) + } + /** - * This is the key used for writing the time zone that should be used if - * home time zones are enabled for the Calendar app. + * A helper method for writing a String value to the preferences + * asynchronously. + * + * @param context A context with access to the correct preferences + * @param key The preference to write to + * @param value The value to write */ - public static final String KEY_HOME_TZ = "preferences_home_tz"; + @JvmStatic + fun setSharedPreference(prefs: SharedPreferences, key: String?, value: String?) { + val editor: SharedPreferences.Editor = prefs.edit() + editor.putString(key, value) + editor.apply() + } + } + /** + * This class contains methods specific to reading and writing time zone + * values. + */ + class TimeZoneUtils + /** + * The name of the file where the shared prefs for Calendar are stored + * must be provided. All activities within an app should provide the + * same preferences name or behavior may become erratic. + * + * @param prefsName + */( // The name of the shared preferences file. This name must be maintained for historical + // reasons, as it's what PreferenceManager assigned the first time the file was created. + private val mPrefsName: String) { /** * This is a helper class for handling the async queries and updates for the * time zone settings in Calendar. */ - private class AsyncTZHandler extends AsyncQueryHandler { - public AsyncTZHandler(ContentResolver cr) { - super(cr); - } - - @Override - protected void onQueryComplete(int token, Object cookie, Cursor cursor) { - synchronized (mTZCallbacks) { + private inner class AsyncTZHandler(cr: ContentResolver?) : AsyncQueryHandler(cr) { + protected override fun onQueryComplete(token: Int, cookie: Any?, cursor: Cursor?) { + synchronized(mTZCallbacks) { if (cursor == null) { - mTZQueryInProgress = false; - mFirstTZRequest = true; - return; + mTZQueryInProgress = false + mFirstTZRequest = true + return } - - boolean writePrefs = false; + var writePrefs = false // Check the values in the db - int keyColumn = cursor.getColumnIndexOrThrow(CalendarCache.KEY); - int valueColumn = cursor.getColumnIndexOrThrow(CalendarCache.VALUE); - while(cursor.moveToNext()) { - String key = cursor.getString(keyColumn); - String value = cursor.getString(valueColumn); + val keyColumn: Int = cursor.getColumnIndexOrThrow(CalendarCache.KEY) + val valueColumn: Int = cursor.getColumnIndexOrThrow(CalendarCache.VALUE) + while (cursor.moveToNext()) { + val key: String = cursor.getString(keyColumn) + val value: String = cursor.getString(valueColumn) if (TextUtils.equals(key, CalendarCache.KEY_TIMEZONE_TYPE)) { - boolean useHomeTZ = !TextUtils.equals( - value, CalendarCache.TIMEZONE_TYPE_AUTO); + val useHomeTZ: Boolean = !TextUtils.equals( + value, CalendarCache.TIMEZONE_TYPE_AUTO) if (useHomeTZ != mUseHomeTZ) { - writePrefs = true; - mUseHomeTZ = useHomeTZ; + writePrefs = true + mUseHomeTZ = useHomeTZ } } else if (TextUtils.equals( - key, CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { + key, CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { if (!TextUtils.isEmpty(value) && !TextUtils.equals(mHomeTZ, value)) { - writePrefs = true; - mHomeTZ = value; + writePrefs = true + mHomeTZ = value } } } - cursor.close(); + cursor.close() if (writePrefs) { - SharedPreferences prefs = getSharedPreferences((Context)cookie, mPrefsName); + val prefs: SharedPreferences = + getSharedPreferences(cookie as Context, mPrefsName) // Write the prefs - setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ); - setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ); + setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ) + setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ) } - - mTZQueryInProgress = false; - for (Runnable callback : mTZCallbacks) { + mTZQueryInProgress = false + for (callback in mTZCallbacks) { if (callback != null) { - callback.run(); + callback.run() } } - mTZCallbacks.clear(); + mTZCallbacks.clear() } } } - /** - * The name of the file where the shared prefs for Calendar are stored - * must be provided. All activities within an app should provide the - * same preferences name or behavior may become erratic. - * - * @param prefsName - */ - public TimeZoneUtils(String prefsName) { - mPrefsName = prefsName; - } - /** * Formats a date or a time range according to the local conventions. * * This formats a date/time range using Calendar's time zone and the * local conventions for the region of the device. * - * If the {@link DateUtils#FORMAT_UTC} flag is used it will pass in + * If the [DateUtils.FORMAT_UTC] flag is used it will pass in * the UTC time zone instead. * * @param context the context is required only if the time is shown * @param startMillis the start time in UTC milliseconds * @param endMillis the end time in UTC milliseconds * @param flags a bit mask of options See - * {@link DateUtils#formatDateRange(Context, Formatter, long, long, int, String) formatDateRange} + * [formatDateRange][DateUtils.formatDateRange] * @return a string containing the formatted date/time range. */ - public String formatDateRange(Context context, long startMillis, - long endMillis, int flags) { - String date; - String tz; - if ((flags & DateUtils.FORMAT_UTC) != 0) { - tz = Time.TIMEZONE_UTC; + fun formatDateRange(context: Context, startMillis: Long, + endMillis: Long, flags: Int): String { + var date: String + val tz: String + tz = if (flags and DateUtils.FORMAT_UTC !== 0) { + Time.TIMEZONE_UTC } else { - tz = getTimeZone(context, null); + getTimeZone(context, null) } - synchronized (mSB) { - mSB.setLength(0); + synchronized(mSB) { + mSB.setLength(0) date = DateUtils.formatDateRange(context, mF, startMillis, endMillis, flags, - tz).toString(); + tz).toString() } - return date; + return date } /** @@ -189,66 +185,65 @@ public class CalendarUtils { * * Updates the home time zone in the db asynchronously and updates * the local cache. Sending a time zone of - * {@link CalendarCache#TIMEZONE_TYPE_AUTO} will cause it to be set + * [CalendarCache.TIMEZONE_TYPE_AUTO] will cause it to be set * to the device's time zone. null or empty tz will be ignored. * * @param context The calling activity * @param timeZone The time zone to set Calendar to, or - * {@link CalendarCache#TIMEZONE_TYPE_AUTO} + * [CalendarCache.TIMEZONE_TYPE_AUTO] */ - public void setTimeZone(Context context, String timeZone) { + fun setTimeZone(context: Context, timeZone: String) { if (TextUtils.isEmpty(timeZone)) { if (DEBUG) { - Log.d(TAG, "Empty time zone, nothing to be done."); + Log.d(TAG, "Empty time zone, nothing to be done.") } - return; + return } - boolean updatePrefs = false; - synchronized (mTZCallbacks) { + var updatePrefs = false + synchronized(mTZCallbacks) { if (CalendarCache.TIMEZONE_TYPE_AUTO.equals(timeZone)) { if (mUseHomeTZ) { - updatePrefs = true; + updatePrefs = true } - mUseHomeTZ = false; + mUseHomeTZ = false } else { if (!mUseHomeTZ || !TextUtils.equals(mHomeTZ, timeZone)) { - updatePrefs = true; + updatePrefs = true } - mUseHomeTZ = true; - mHomeTZ = timeZone; + mUseHomeTZ = true + mHomeTZ = timeZone } } if (updatePrefs) { // Write the prefs - SharedPreferences prefs = getSharedPreferences(context, mPrefsName); - setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ); - setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ); + val prefs: SharedPreferences = getSharedPreferences(context, mPrefsName) + setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ) + setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ) // Update the db - ContentValues values = new ContentValues(); + val values = ContentValues() if (mHandler != null) { - mHandler.cancelOperation(mToken); + mHandler?.cancelOperation(mToken) } - - mHandler = new AsyncTZHandler(context.getContentResolver()); + mHandler = AsyncTZHandler(context.getContentResolver()) // skip 0 so query can use it if (++mToken == 0) { - mToken = 1; + mToken = 1 } // Write the use home tz setting - values.put(CalendarCache.VALUE, mUseHomeTZ ? CalendarCache.TIMEZONE_TYPE_HOME - : CalendarCache.TIMEZONE_TYPE_AUTO); - mHandler.startUpdate(mToken, null, CalendarCache.URI, values, "key=?", - TIMEZONE_TYPE_ARGS); + values.put(CalendarCache.VALUE, if (mUseHomeTZ) CalendarCache.TIMEZONE_TYPE_HOME + else CalendarCache.TIMEZONE_TYPE_AUTO) + mHandler?.startUpdate(mToken, null, CalendarCache.URI, values, "key=?", + TIMEZONE_TYPE_ARGS) // If using a home tz write it to the db if (mUseHomeTZ) { - ContentValues values2 = new ContentValues(); - values2.put(CalendarCache.VALUE, mHomeTZ); - mHandler.startUpdate(mToken, null, CalendarCache.URI, values2, - "key=?", TIMEZONE_INSTANCES_ARGS); + val values2 = ContentValues() + values2.put(CalendarCache.VALUE, mHomeTZ) + mHandler?.startUpdate(mToken, null, CalendarCache.URI, values2, + "key=?", TIMEZONE_INSTANCES_ARGS) } } } @@ -267,34 +262,34 @@ public class CalendarUtils { * @param callback The runnable that should execute if a query returns new values * @return The string value representing the time zone Calendar should display */ - public String getTimeZone(Context context, Runnable callback) { - synchronized (mTZCallbacks){ + fun getTimeZone(context: Context, callback: Runnable?): String { + synchronized(mTZCallbacks) { if (mFirstTZRequest) { - SharedPreferences prefs = getSharedPreferences(context, mPrefsName); - mUseHomeTZ = prefs.getBoolean(KEY_HOME_TZ_ENABLED, false); - mHomeTZ = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()); + val prefs: SharedPreferences = getSharedPreferences(context, mPrefsName) + mUseHomeTZ = prefs.getBoolean(KEY_HOME_TZ_ENABLED, false) + mHomeTZ = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()) ?: String() // Only check content resolver if we have a looper to attach to use if (Looper.myLooper() != null) { - mTZQueryInProgress = true; - mFirstTZRequest = false; + mTZQueryInProgress = true + mFirstTZRequest = false // When the async query returns it should synchronize on // mTZCallbacks, update mUseHomeTZ, mHomeTZ, and the // preferences, set mTZQueryInProgress to false, and call all // the runnables in mTZCallbacks. if (mHandler == null) { - mHandler = new AsyncTZHandler(context.getContentResolver()); + mHandler = AsyncTZHandler(context.getContentResolver()) } - mHandler.startQuery(0, context, CalendarCache.URI, CALENDAR_CACHE_POJECTION, - null, null, null); + mHandler?.startQuery(0, context, CalendarCache.URI, + CALENDAR_CACHE_POJECTION, null, null, null) } } - if (mTZQueryInProgress) { - mTZCallbacks.add(callback); + if (mTZQueryInProgress && callback != null) { + mTZCallbacks.add(callback) } } - return mUseHomeTZ ? mHomeTZ : Time.getCurrentTimezone(); + return if (mUseHomeTZ) mHomeTZ else Time.getCurrentTimezone() } /** @@ -305,52 +300,55 @@ public class CalendarUtils { * * @param context The calling activity * @param callback The runnable that should execute if a query returns - * new values + * new values */ - public void forceDBRequery(Context context, Runnable callback) { - synchronized (mTZCallbacks){ + fun forceDBRequery(context: Context, callback: Runnable) { + synchronized(mTZCallbacks) { if (mTZQueryInProgress) { - mTZCallbacks.add(callback); - return; + mTZCallbacks.add(callback) + return } - mFirstTZRequest = true; - getTimeZone(context, callback); + mFirstTZRequest = true + getTimeZone(context, callback) } } - } - /** - * A helper method for writing a String value to the preferences - * asynchronously. - * - * @param context A context with access to the correct preferences - * @param key The preference to write to - * @param value The value to write - */ - public static void setSharedPreference(SharedPreferences prefs, String key, String value) { -// SharedPreferences prefs = getSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - editor.putString(key, value); - editor.apply(); - } + companion object { + private val TIMEZONE_TYPE_ARGS = arrayOf(CalendarCache.KEY_TIMEZONE_TYPE) + private val TIMEZONE_INSTANCES_ARGS = + arrayOf(CalendarCache.KEY_TIMEZONE_INSTANCES) + val CALENDAR_CACHE_POJECTION = arrayOf( + CalendarCache.KEY, CalendarCache.VALUE + ) + private val mSB: StringBuilder = StringBuilder(50) + private val mF: Formatter = Formatter(mSB, Locale.getDefault()) - /** - * A helper method for writing a boolean value to the preferences - * asynchronously. - * - * @param context A context with access to the correct preferences - * @param key The preference to write to - * @param value The value to write - */ - public static void setSharedPreference(SharedPreferences prefs, String key, boolean value) { -// SharedPreferences prefs = getSharedPreferences(context, prefsName); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(key, value); - editor.apply(); - } + @Volatile + private var mFirstTZRequest = true + + @Volatile + private var mTZQueryInProgress = false - /** Return a properly configured SharedPreferences instance */ - public static SharedPreferences getSharedPreferences(Context context, String prefsName) { - return context.getSharedPreferences(prefsName, Context.MODE_PRIVATE); + @Volatile + private var mUseHomeTZ = false + + @Volatile + private var mHomeTZ: String = Time.getCurrentTimezone() + private val mTZCallbacks: HashSet = HashSet() + private var mToken = 1 + private var mHandler: AsyncTZHandler? = null + + /** + * This is the key used for writing whether or not a home time zone should + * be used in the Calendar app to the Calendar Preferences. + */ + const val KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled" + + /** + * This is the key used for writing the time zone that should be used if + * home time zones are enabled for the Calendar app. + */ + const val KEY_HOME_TZ = "preferences_home_tz" } -} + } +} \ No newline at end of file -- cgit v1.2.3 From 0e3d2bac96af699c03a9881fa39cc7ae332202c9 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Tue, 15 Jun 2021 17:14:49 +0000 Subject: AOSP/Calendar - Copy of NotificationMgr.java and QuickResponseActivity.java The Java code in NotifcationMgr.java and QuickResponseActivity.java has been copied into NotificationMgr.kt and QuickResponseActivity.kt respectively. Test: manual - opening both files shows that they are identical. Change-Id: I859e7f34ad8a1574385431b5d4cc2296e1cfa527 --- src/com/android/calendar/alerts/NotificationMgr.kt | 41 ++++++++ .../calendar/alerts/QuickResponseActivity.kt | 108 +++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 src/com/android/calendar/alerts/NotificationMgr.kt create mode 100644 src/com/android/calendar/alerts/QuickResponseActivity.kt diff --git a/src/com/android/calendar/alerts/NotificationMgr.kt b/src/com/android/calendar/alerts/NotificationMgr.kt new file mode 100644 index 00000000..0ab475c3 --- /dev/null +++ b/src/com/android/calendar/alerts/NotificationMgr.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012 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.alerts; + +import com.android.calendar.alerts.AlertService.NotificationWrapper; + +public abstract class NotificationMgr { + public abstract void notify(int id, NotificationWrapper notification); + public abstract void cancel(int id); + + /** + * Don't actually use the notification framework's cancelAll since the SyncAdapter + * might post notifications and we don't want to affect those. + */ + public void cancelAll() { + cancelAllBetween(0, AlertService.MAX_NOTIFICATIONS); + } + + /** + * Cancels IDs between the specified bounds, inclusively. + */ + public void cancelAllBetween(int from, int to) { + for (int i = from; i <= to; i++) { + cancel(i); + } + } +} diff --git a/src/com/android/calendar/alerts/QuickResponseActivity.kt b/src/com/android/calendar/alerts/QuickResponseActivity.kt new file mode 100644 index 00000000..3d291d02 --- /dev/null +++ b/src/com/android/calendar/alerts/QuickResponseActivity.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2012 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.alerts; + +import android.app.ListActivity; +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.Toast; + +import com.android.calendar.R; +import com.android.calendar.Utils; + +import java.util.Arrays; + +/** + * Activity which displays when the user wants to email guests from notifications. + * + * This presents the user with list if quick responses to be populated in an email + * to minimize typing. + * + */ +public class QuickResponseActivity extends ListActivity implements OnItemClickListener { + private static final String TAG = "QuickResponseActivity"; + public static final String EXTRA_EVENT_ID = "eventId"; + + private String[] mResponses = null; + static long mEventId; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + Intent intent = getIntent(); + if (intent == null) { + finish(); + return; + } + + mEventId = intent.getLongExtra(EXTRA_EVENT_ID, -1); + if (mEventId == -1) { + finish(); + return; + } + + // Set listener + getListView().setOnItemClickListener(QuickResponseActivity.this); + + // Populate responses + String[] responses = Utils.getQuickResponses(this); + Arrays.sort(responses); + + // Add "Custom response..." + mResponses = new String[responses.length + 1]; + int i; + for (i = 0; i < responses.length; i++) { + mResponses[i] = responses[i]; + } + mResponses[i] = getResources().getString(R.string.quick_response_custom_msg); + + setListAdapter(new ArrayAdapter(this, R.layout.quick_response_item, mResponses)); + } + + // implements OnItemClickListener + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + + String body = null; + if (mResponses != null && position < mResponses.length - 1) { + body = mResponses[position]; + } + + // Start thread to query provider and send mail + new QueryThread(mEventId, body).start(); + } + + private class QueryThread extends Thread { + long mEventId; + String mBody; + + QueryThread(long eventId, String body) { + mEventId = eventId; + mBody = body; + } + + @Override + public void run() { + } + } +} -- cgit v1.2.3 From 8e6783da0da8f35f520247ec02883fc81ca042cf Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Wed, 16 Jun 2021 19:39:38 +0000 Subject: AOSP/Calendar - NotificationMgr, QuickResponseAcitivty, AlertService tweak, bp file Using Android Studio, NotificationMgr.java and QuickResponseActivity.java has been fully converted to Kotlin. Since a static inner class in AlertService.kt extends NotificationMgr.kt, AlertService.kt had to be slightly tweaked in order for both files to properly work together. Since the second parameter of the abstract notify method is nullable, this change had to be reflected in AlertService.kt in which the nullable pararmeter had to safe call functions. Another tweak that had to be made in QuickResponseActivity.kt which dealt with the casting of mResponses as a parameter of the function setListAdapter. Again, the Android Studio converter has proven to fail at automatically detecting when to cast to Array as to avoid compile errors that have to do with type mismatch. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.188 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 14m 2s Total aggregated tests run time: 14m 2s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.45 tests/sec [376 tests / 842869 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 6645 ms || clean = 1743 ms Total preparation time: 6s || Total tear down time: 1s ======================================================= =============== Summary =============== Total Run time: 15m 23s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: I14bc9fa469eee3abfc7d50de85d5e3e0c2fce619 --- Android.bp | 2 + src/com/android/calendar/alerts/AlertService.kt | 4 +- src/com/android/calendar/alerts/NotificationMgr.kt | 25 +++-- .../calendar/alerts/QuickResponseActivity.kt | 114 +++++++++------------ 4 files changed, 67 insertions(+), 78 deletions(-) diff --git a/Android.bp b/Android.bp index ded1dc55..a7348a6d 100644 --- a/Android.bp +++ b/Android.bp @@ -22,6 +22,8 @@ exclude_srcsd = [ "src/**/calendar/alerts/AlertReceiver.java", "src/**/calendar/alerts/AlertService.java", "src/**/calendar/alerts/AlertUtils.java", + "src/**/calendar/alerts/NotificationMgr.java", + "src/**/calendar/alerts/QuickResponseActivity.java", "src/**/calendar/DayOfMonthDrawable.java", ] diff --git a/src/com/android/calendar/alerts/AlertService.kt b/src/com/android/calendar/alerts/AlertService.kt index 2cb9a268..bc1b4e04 100644 --- a/src/com/android/calendar/alerts/AlertService.kt +++ b/src/com/android/calendar/alerts/AlertService.kt @@ -78,8 +78,8 @@ class AlertService : Service() { } @Override - override fun notify(id: Int, nw: NotificationWrapper) { - mNm.notify(id, nw.mNotification) + override fun notify(id: Int, nw: NotificationWrapper?) { + mNm.notify(id, nw?.mNotification) } init { diff --git a/src/com/android/calendar/alerts/NotificationMgr.kt b/src/com/android/calendar/alerts/NotificationMgr.kt index 0ab475c3..609b8141 100644 --- a/src/com/android/calendar/alerts/NotificationMgr.kt +++ b/src/com/android/calendar/alerts/NotificationMgr.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,29 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar.alerts -package com.android.calendar.alerts; +import com.android.calendar.alerts.AlertService.NotificationWrapper -import com.android.calendar.alerts.AlertService.NotificationWrapper; - -public abstract class NotificationMgr { - public abstract void notify(int id, NotificationWrapper notification); - public abstract void cancel(int id); +abstract class NotificationMgr { + abstract fun notify(id: Int, notification: NotificationWrapper?) + abstract fun cancel(id: Int) /** * Don't actually use the notification framework's cancelAll since the SyncAdapter * might post notifications and we don't want to affect those. */ - public void cancelAll() { - cancelAllBetween(0, AlertService.MAX_NOTIFICATIONS); + fun cancelAll() { + cancelAllBetween(0, AlertService.MAX_NOTIFICATIONS) } /** * Cancels IDs between the specified bounds, inclusively. */ - public void cancelAllBetween(int from, int to) { - for (int i = from; i <= to; i++) { - cancel(i); + fun cancelAllBetween(from: Int, to: Int) { + for (i in from..to) { + cancel(i) } } -} +} \ No newline at end of file diff --git a/src/com/android/calendar/alerts/QuickResponseActivity.kt b/src/com/android/calendar/alerts/QuickResponseActivity.kt index 3d291d02..a3d27e83 100644 --- a/src/com/android/calendar/alerts/QuickResponseActivity.kt +++ b/src/com/android/calendar/alerts/QuickResponseActivity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,23 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar.alerts; - -import android.app.ListActivity; -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.Toast; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.Arrays; +package com.android.calendar.alerts + +import android.app.ListActivity +import android.content.Intent +import android.os.Bundle +import android.view.View +import android.widget.AdapterView +import android.widget.AdapterView.OnItemClickListener +import android.widget.ArrayAdapter +import com.android.calendar.R +import com.android.calendar.Utils +import java.util.Arrays /** * Activity which displays when the user wants to email guests from notifications. @@ -38,71 +33,64 @@ import java.util.Arrays; * to minimize typing. * */ -public class QuickResponseActivity extends ListActivity implements OnItemClickListener { - private static final String TAG = "QuickResponseActivity"; - public static final String EXTRA_EVENT_ID = "eventId"; - - private String[] mResponses = null; - static long mEventId; - +class QuickResponseActivity : ListActivity(), OnItemClickListener { + private var mResponses: Array? = null @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - Intent intent = getIntent(); + protected override fun onCreate(icicle: Bundle?) { + super.onCreate(icicle) + val intent: Intent = getIntent() if (intent == null) { - finish(); - return; + finish() + return } - - mEventId = intent.getLongExtra(EXTRA_EVENT_ID, -1); - if (mEventId == -1) { - finish(); - return; + mEventId = intent.getLongExtra(EXTRA_EVENT_ID, -1) + if (mEventId == -1L) { + finish() + return } // Set listener - getListView().setOnItemClickListener(QuickResponseActivity.this); + getListView().setOnItemClickListener(this@QuickResponseActivity) // Populate responses - String[] responses = Utils.getQuickResponses(this); - Arrays.sort(responses); + val responses: Array = Utils.getQuickResponses(this) + Arrays.sort(responses) // Add "Custom response..." - mResponses = new String[responses.length + 1]; - int i; - for (i = 0; i < responses.length; i++) { - mResponses[i] = responses[i]; + mResponses = arrayOfNulls(responses.size + 1) + var i: Int + i = 0 + while (i < responses.size) { + mResponses!![i] = responses[i] + i++ } - mResponses[i] = getResources().getString(R.string.quick_response_custom_msg); - - setListAdapter(new ArrayAdapter(this, R.layout.quick_response_item, mResponses)); + mResponses!![i] = getResources().getString(R.string.quick_response_custom_msg) + setListAdapter(ArrayAdapter(this, R.layout.quick_response_item, + mResponses as Array)) } // implements OnItemClickListener @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - - String body = null; - if (mResponses != null && position < mResponses.length - 1) { - body = mResponses[position]; + override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + var body: String? = null + if (mResponses != null && position < mResponses!!.size - 1) { + body = mResponses!![position] } // Start thread to query provider and send mail - new QueryThread(mEventId, body).start(); + QueryThread(mEventId, body).start() } - private class QueryThread extends Thread { - long mEventId; - String mBody; - - QueryThread(long eventId, String body) { - mEventId = eventId; - mBody = body; - } - + private inner class QueryThread internal constructor(var mEventId: Long, var mBody: String?) : + Thread() { @Override - public void run() { + override fun run() { } } -} + + companion object { + private const val TAG = "QuickResponseActivity" + const val EXTRA_EVENT_ID = "eventId" + var mEventId: Long = 0 + } +} \ No newline at end of file -- cgit v1.2.3 From 52e086993a38e2d8b64acc49861ba7a32650d1c1 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Wed, 16 Jun 2021 21:24:55 +0000 Subject: AOSP/Calendar - Copy of DismissAlarmsService, GlobalDismissManager, and InitAlarmsService The Java code in DismissAlarmsService.java, GlobalDismissManager, and InitAlarmsService have all been copied to their corresponding .kt files. Test: manual - opening the files shows that .java file and corresponding .kt file are identical. Change-Id: I0c6cb51dd5b7a0e871834f880a557cb9ecadfc46 --- .../calendar/alerts/DismissAlarmsService.kt | 136 +++++++++++++++++++++ .../calendar/alerts/GlobalDismissManager.kt | 84 +++++++++++++ .../android/calendar/alerts/InitAlarmsService.kt | 62 ++++++++++ 3 files changed, 282 insertions(+) create mode 100644 src/com/android/calendar/alerts/DismissAlarmsService.kt create mode 100644 src/com/android/calendar/alerts/GlobalDismissManager.kt create mode 100644 src/com/android/calendar/alerts/InitAlarmsService.kt diff --git a/src/com/android/calendar/alerts/DismissAlarmsService.kt b/src/com/android/calendar/alerts/DismissAlarmsService.kt new file mode 100644 index 00000000..1ec3c22d --- /dev/null +++ b/src/com/android/calendar/alerts/DismissAlarmsService.kt @@ -0,0 +1,136 @@ +/* + * 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.alerts; + +import android.app.IntentService; +import android.app.NotificationManager; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.IBinder; +import android.provider.CalendarContract.CalendarAlerts; +import androidx.core.app.TaskStackBuilder; + +import android.util.Log; +import com.android.calendar.EventInfoActivity; +import com.android.calendar.alerts.GlobalDismissManager.AlarmId; + +import java.util.LinkedList; +import java.util.List; + +/** + * Service for asynchronously marking fired alarms as dismissed. + */ +public class DismissAlarmsService extends IntentService { + private static final String TAG = "DismissAlarmsService"; + public static final String SHOW_ACTION = "com.android.calendar.SHOW"; + public static final String DISMISS_ACTION = "com.android.calendar.DISMISS"; + + private static final String[] PROJECTION = new String[] { + CalendarAlerts.STATE, + }; + private static final int COLUMN_INDEX_STATE = 0; + + public DismissAlarmsService() { + super("DismissAlarmsService"); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onHandleIntent(Intent intent) { + if (AlertService.DEBUG) { + Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); + } + + long eventId = intent.getLongExtra(AlertUtils.EVENT_ID_KEY, -1); + long eventStart = intent.getLongExtra(AlertUtils.EVENT_START_KEY, -1); + long eventEnd = intent.getLongExtra(AlertUtils.EVENT_END_KEY, -1); + long[] eventIds = intent.getLongArrayExtra(AlertUtils.EVENT_IDS_KEY); + long[] eventStarts = intent.getLongArrayExtra(AlertUtils.EVENT_STARTS_KEY); + int notificationId = intent.getIntExtra(AlertUtils.NOTIFICATION_ID_KEY, -1); + List alarmIds = new LinkedList(); + + Uri uri = CalendarAlerts.CONTENT_URI; + String selection; + + // Dismiss a specific fired alarm if id is present, otherwise, dismiss all alarms + if (eventId != -1) { + alarmIds.add(new AlarmId(eventId, eventStart)); + selection = CalendarAlerts.STATE + "=" + CalendarAlerts.STATE_FIRED + " AND " + + CalendarAlerts.EVENT_ID + "=" + eventId; + } else if (eventIds != null && eventIds.length > 0 && + eventStarts != null && eventIds.length == eventStarts.length) { + selection = buildMultipleEventsQuery(eventIds); + for (int i = 0; i < eventIds.length; i++) { + alarmIds.add(new AlarmId(eventIds[i], eventStarts[i])); + } + } else { + // NOTE: I don't believe that this ever happens. + selection = CalendarAlerts.STATE + "=" + CalendarAlerts.STATE_FIRED; + } + + GlobalDismissManager.dismissGlobally(getApplicationContext(), alarmIds); + + ContentResolver resolver = getContentResolver(); + ContentValues values = new ContentValues(); + values.put(PROJECTION[COLUMN_INDEX_STATE], CalendarAlerts.STATE_DISMISSED); + resolver.update(uri, values, selection, null); + + // Remove from notification bar. + if (notificationId != -1) { + NotificationManager nm = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + nm.cancel(notificationId); + } + + if (SHOW_ACTION.equals(intent.getAction())) { + // Show event on Calendar app by building an intent and task stack to start + // EventInfoActivity with AllInOneActivity as the parent activity rooted to home. + Intent i = AlertUtils.buildEventViewIntent(this, eventId, eventStart, eventEnd); + + TaskStackBuilder.create(this) + .addParentStack(EventInfoActivity.class).addNextIntent(i).startActivities(); + } + } + + private String buildMultipleEventsQuery(long[] eventIds) { + StringBuilder selection = new StringBuilder(); + selection.append(CalendarAlerts.STATE); + selection.append("="); + selection.append(CalendarAlerts.STATE_FIRED); + if (eventIds.length > 0) { + selection.append(" AND ("); + selection.append(CalendarAlerts.EVENT_ID); + selection.append("="); + selection.append(eventIds[0]); + for (int i = 1; i < eventIds.length; i++) { + selection.append(" OR "); + selection.append(CalendarAlerts.EVENT_ID); + selection.append("="); + selection.append(eventIds[i]); + } + selection.append(")"); + } + return selection.toString(); + } +} diff --git a/src/com/android/calendar/alerts/GlobalDismissManager.kt b/src/com/android/calendar/alerts/GlobalDismissManager.kt new file mode 100644 index 00000000..27b3e162 --- /dev/null +++ b/src/com/android/calendar/alerts/GlobalDismissManager.kt @@ -0,0 +1,84 @@ +/* + * 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.alerts; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.provider.CalendarContract.CalendarAlerts; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Events; +import android.util.Log; +import android.util.Pair; + +import com.android.calendar.R; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Utilities for managing notification dismissal across devices. + */ +public class GlobalDismissManager extends BroadcastReceiver { + public static class AlarmId { + public long mEventId; + public long mStart; + + public AlarmId(long id, long start) { + mEventId = id; + mStart = start; + } + } + + /** + * Globally dismiss notifications that are backed by the same events. + * + * @param context application context + * @param alarmIds Unique identifiers for events that have been dismissed by the user. + * @return true if notification_sender_id is available + */ + public static void dismissGlobally(Context context, List alarmIds) { + Set eventIds = new HashSet(alarmIds.size()); + for (AlarmId alarmId: alarmIds) { + eventIds.add(alarmId.mEventId); + } + } + + @Override + @SuppressWarnings("unchecked") + public void onReceive(Context context, Intent intent) { + new AsyncTask, Void, Void>() { + @Override + protected Void doInBackground(Pair... params) { + return null; + } + }.execute(new Pair(context, intent)); + } +} diff --git a/src/com/android/calendar/alerts/InitAlarmsService.kt b/src/com/android/calendar/alerts/InitAlarmsService.kt new file mode 100644 index 00000000..3a9b0b2c --- /dev/null +++ b/src/com/android/calendar/alerts/InitAlarmsService.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 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.alerts; + +import android.app.IntentService; +import android.content.ContentValues; +import android.content.Intent; +import android.net.Uri; +import android.os.SystemClock; +import android.provider.CalendarContract; +import android.util.Log; + +/** + * Service for clearing all scheduled alerts from the CalendarAlerts table and + * rescheduling them. This is expected to be called only on boot up, to restore + * the AlarmManager alarms that were lost on device restart. + */ +public class InitAlarmsService extends IntentService { + private static final String TAG = "InitAlarmsService"; + private static final String SCHEDULE_ALARM_REMOVE_PATH = "schedule_alarms_remove"; + private static final Uri SCHEDULE_ALARM_REMOVE_URI = Uri.withAppendedPath( + CalendarContract.CONTENT_URI, SCHEDULE_ALARM_REMOVE_PATH); + + // Delay for rescheduling the alarms must be great enough to minimize race + // conditions with the provider's boot up actions. + private static final long DELAY_MS = 30000; + + public InitAlarmsService() { + super("InitAlarmsService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + // Delay to avoid race condition of in-progress alarm scheduling in provider. + SystemClock.sleep(DELAY_MS); + Log.d(TAG, "Clearing and rescheduling alarms."); + try { + getContentResolver().update(SCHEDULE_ALARM_REMOVE_URI, new ContentValues(), null, + null); + } catch (java.lang.IllegalArgumentException e) { + // java.lang.IllegalArgumentException: + // Unknown URI content://com.android.calendar/schedule_alarms_remove + + // Until b/7742576 is resolved, just catch the exception so the app won't crash + Log.e(TAG, "update failed: " + e.toString()); + } + } +} -- cgit v1.2.3 From dd8912065740cae077086abb440d861048aad6e5 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Wed, 16 Jun 2021 22:37:14 +0000 Subject: AOSP/Calendar - DismissAlarmsService, GlobalDismissManager, InitAlarmsService, bp file Using Android Studio, DismissAlarmsService, GlobalDismissManager, and InitAlarmsService were fully converted to Kotlin. I made it a point to ensure that DismissAlarmsManager was converted last since it depended on GlobalDismissManager. The converter has not been doing a great job at recognizing that if the static and dynamic type of an object reference in Java do not match, you mustn't set the type of the corresponding Kotlin object to the static type since it will cause a type mismatch compile error. For instance, if Set s = new HashSet(), the Kotlin type for variable s must be HashSet. In other words, the inferred type and explicit type must match. The converter did not take this into consideration for some variables in GlobalDismissManager. Another general issue I've come across has to do with the fact that the "override" keyword is not automatically inserted for classes implementing an abstract methods. Finally, the @JvmStatic had to be applied manually in order to enforce the static behavior of methods. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.144 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 8m 19s Total aggregated tests run time: 8m 19s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.75 tests/sec [376 tests / 499590 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 6098 ms || clean = 1820 ms Total preparation time: 6s || Total tear down time: 1s ======================================================= =============== Summary =============== Total Run time: 9m 44s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: I39ddedf1082f456977d4bb06c3ac3c17d761bcfd --- Android.bp | 3 + .../calendar/alerts/DismissAlarmsService.kt | 177 ++++++++++----------- .../calendar/alerts/GlobalDismissManager.kt | 92 ++++------- .../android/calendar/alerts/InitAlarmsService.kt | 64 ++++---- 4 files changed, 152 insertions(+), 184 deletions(-) diff --git a/Android.bp b/Android.bp index a7348a6d..dafa3c63 100644 --- a/Android.bp +++ b/Android.bp @@ -22,6 +22,9 @@ exclude_srcsd = [ "src/**/calendar/alerts/AlertReceiver.java", "src/**/calendar/alerts/AlertService.java", "src/**/calendar/alerts/AlertUtils.java", + "src/**/calendar/alerts/DismissAlarmsService.java", + "src/**/calendar/alerts/GlobalDismissManager.java", + "src/**/calendar/alerts/InitAlarmsService.java", "src/**/calendar/alerts/NotificationMgr.java", "src/**/calendar/alerts/QuickResponseActivity.java", "src/**/calendar/DayOfMonthDrawable.java", diff --git a/src/com/android/calendar/alerts/DismissAlarmsService.kt b/src/com/android/calendar/alerts/DismissAlarmsService.kt index 1ec3c22d..88683d3a 100644 --- a/src/com/android/calendar/alerts/DismissAlarmsService.kt +++ b/src/com/android/calendar/alerts/DismissAlarmsService.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,124 +13,115 @@ * See the License for the specific language governing permissions and * limitations under the License */ - -package com.android.calendar.alerts; - -import android.app.IntentService; -import android.app.NotificationManager; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.IBinder; -import android.provider.CalendarContract.CalendarAlerts; -import androidx.core.app.TaskStackBuilder; - -import android.util.Log; -import com.android.calendar.EventInfoActivity; -import com.android.calendar.alerts.GlobalDismissManager.AlarmId; - -import java.util.LinkedList; -import java.util.List; +package com.android.calendar.alerts + +import android.app.IntentService +import android.app.NotificationManager +import android.content.ContentResolver +import android.content.ContentValues +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.IBinder +import android.provider.CalendarContract.CalendarAlerts +import androidx.core.app.TaskStackBuilder +import android.util.Log +import com.android.calendar.EventInfoActivity +import com.android.calendar.alerts.GlobalDismissManager.AlarmId +import java.util.LinkedList +import java.util.List /** * Service for asynchronously marking fired alarms as dismissed. */ -public class DismissAlarmsService extends IntentService { - private static final String TAG = "DismissAlarmsService"; - public static final String SHOW_ACTION = "com.android.calendar.SHOW"; - public static final String DISMISS_ACTION = "com.android.calendar.DISMISS"; - - private static final String[] PROJECTION = new String[] { - CalendarAlerts.STATE, - }; - private static final int COLUMN_INDEX_STATE = 0; - - public DismissAlarmsService() { - super("DismissAlarmsService"); - } - +class DismissAlarmsService : IntentService("DismissAlarmsService") { @Override - public IBinder onBind(Intent intent) { - return null; + override fun onBind(intent: Intent?): IBinder? { + return null } @Override - public void onHandleIntent(Intent intent) { + override fun onHandleIntent(intent: Intent?) { if (AlertService.DEBUG) { - Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); + Log.d(TAG, "onReceive: a=" + intent?.getAction().toString() + " " + intent.toString()) } - - long eventId = intent.getLongExtra(AlertUtils.EVENT_ID_KEY, -1); - long eventStart = intent.getLongExtra(AlertUtils.EVENT_START_KEY, -1); - long eventEnd = intent.getLongExtra(AlertUtils.EVENT_END_KEY, -1); - long[] eventIds = intent.getLongArrayExtra(AlertUtils.EVENT_IDS_KEY); - long[] eventStarts = intent.getLongArrayExtra(AlertUtils.EVENT_STARTS_KEY); - int notificationId = intent.getIntExtra(AlertUtils.NOTIFICATION_ID_KEY, -1); - List alarmIds = new LinkedList(); - - Uri uri = CalendarAlerts.CONTENT_URI; - String selection; + val eventId = intent?.getLongExtra(AlertUtils.EVENT_ID_KEY, -1) + val eventStart = intent?.getLongExtra(AlertUtils.EVENT_START_KEY, -1) + val eventEnd = intent?.getLongExtra(AlertUtils.EVENT_END_KEY, -1) + val eventIds = intent?.getLongArrayExtra(AlertUtils.EVENT_IDS_KEY) + val eventStarts = intent?.getLongArrayExtra(AlertUtils.EVENT_STARTS_KEY) + val notificationId = intent?.getIntExtra(AlertUtils.NOTIFICATION_ID_KEY, -1) + val alarmIds = LinkedList() + val uri: Uri = CalendarAlerts.CONTENT_URI + val selection: String // Dismiss a specific fired alarm if id is present, otherwise, dismiss all alarms - if (eventId != -1) { - alarmIds.add(new AlarmId(eventId, eventStart)); - selection = CalendarAlerts.STATE + "=" + CalendarAlerts.STATE_FIRED + " AND " + - CalendarAlerts.EVENT_ID + "=" + eventId; - } else if (eventIds != null && eventIds.length > 0 && - eventStarts != null && eventIds.length == eventStarts.length) { - selection = buildMultipleEventsQuery(eventIds); - for (int i = 0; i < eventIds.length; i++) { - alarmIds.add(new AlarmId(eventIds[i], eventStarts[i])); + if (eventId != -1L) { + alarmIds.add(AlarmId(eventId as Long, eventStart as Long)) + selection = + CalendarAlerts.STATE.toString() + "=" + CalendarAlerts.STATE_FIRED + " AND " + + CalendarAlerts.EVENT_ID + "=" + eventId + } else if (eventIds != null && eventIds.size > 0 && eventStarts != null && + eventIds.size == eventStarts.size) { + selection = buildMultipleEventsQuery(eventIds) + for (i in eventIds.indices) { + alarmIds.add(AlarmId(eventIds[i], eventStarts[i])) } } else { // NOTE: I don't believe that this ever happens. - selection = CalendarAlerts.STATE + "=" + CalendarAlerts.STATE_FIRED; + selection = CalendarAlerts.STATE.toString() + "=" + CalendarAlerts.STATE_FIRED } - - GlobalDismissManager.dismissGlobally(getApplicationContext(), alarmIds); - - ContentResolver resolver = getContentResolver(); - ContentValues values = new ContentValues(); - values.put(PROJECTION[COLUMN_INDEX_STATE], CalendarAlerts.STATE_DISMISSED); - resolver.update(uri, values, selection, null); + GlobalDismissManager.dismissGlobally(getApplicationContext(), + alarmIds as List) + val resolver: ContentResolver = getContentResolver() + val values = ContentValues() + values.put(PROJECTION[COLUMN_INDEX_STATE], CalendarAlerts.STATE_DISMISSED) + resolver.update(uri, values, selection, null) // Remove from notification bar. if (notificationId != -1) { - NotificationManager nm = - (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - nm.cancel(notificationId); + val nm: NotificationManager = + getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + nm.cancel(notificationId as Int) } - - if (SHOW_ACTION.equals(intent.getAction())) { + if (SHOW_ACTION.equals(intent?.getAction())) { // Show event on Calendar app by building an intent and task stack to start // EventInfoActivity with AllInOneActivity as the parent activity rooted to home. - Intent i = AlertUtils.buildEventViewIntent(this, eventId, eventStart, eventEnd); - + val i: Intent = AlertUtils.buildEventViewIntent(this, eventId as Long, + eventStart as Long, eventEnd as Long) TaskStackBuilder.create(this) - .addParentStack(EventInfoActivity.class).addNextIntent(i).startActivities(); + .addParentStack(EventInfoActivity::class.java).addNextIntent(i).startActivities() } } - private String buildMultipleEventsQuery(long[] eventIds) { - StringBuilder selection = new StringBuilder(); - selection.append(CalendarAlerts.STATE); - selection.append("="); - selection.append(CalendarAlerts.STATE_FIRED); - if (eventIds.length > 0) { - selection.append(" AND ("); - selection.append(CalendarAlerts.EVENT_ID); - selection.append("="); - selection.append(eventIds[0]); - for (int i = 1; i < eventIds.length; i++) { - selection.append(" OR "); - selection.append(CalendarAlerts.EVENT_ID); - selection.append("="); - selection.append(eventIds[i]); + private fun buildMultipleEventsQuery(eventIds: LongArray): String { + val selection = StringBuilder() + selection.append(CalendarAlerts.STATE) + selection.append("=") + selection.append(CalendarAlerts.STATE_FIRED) + if (eventIds.size > 0) { + selection.append(" AND (") + selection.append(CalendarAlerts.EVENT_ID) + selection.append("=") + selection.append(eventIds[0]) + for (i in 1 until eventIds.size) { + selection.append(" OR ") + selection.append(CalendarAlerts.EVENT_ID) + selection.append("=") + selection.append(eventIds[i]) } - selection.append(")"); + selection.append(")") } - return selection.toString(); + return selection.toString() + } + + companion object { + private const val TAG = "DismissAlarmsService" + const val SHOW_ACTION = "com.android.calendar.SHOW" + const val DISMISS_ACTION = "com.android.calendar.DISMISS" + private val PROJECTION = arrayOf( + CalendarAlerts.STATE + ) + private const val COLUMN_INDEX_STATE = 0 } -} +} \ No newline at end of file diff --git a/src/com/android/calendar/alerts/GlobalDismissManager.kt b/src/com/android/calendar/alerts/GlobalDismissManager.kt index 27b3e162..4cf0bc0c 100644 --- a/src/com/android/calendar/alerts/GlobalDismissManager.kt +++ b/src/com/android/calendar/alerts/GlobalDismissManager.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,72 +13,46 @@ * See the License for the specific language governing permissions and * limitations under the License */ +package com.android.calendar.alerts -package com.android.calendar.alerts; - -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.provider.CalendarContract.CalendarAlerts; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.util.Log; -import android.util.Pair; - -import com.android.calendar.R; - -import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.os.AsyncTask +import android.util.Pair +import java.util.HashSet +import java.util.List /** * Utilities for managing notification dismissal across devices. */ -public class GlobalDismissManager extends BroadcastReceiver { - public static class AlarmId { - public long mEventId; - public long mStart; - - public AlarmId(long id, long start) { - mEventId = id; - mStart = start; - } - } - - /** - * Globally dismiss notifications that are backed by the same events. - * - * @param context application context - * @param alarmIds Unique identifiers for events that have been dismissed by the user. - * @return true if notification_sender_id is available - */ - public static void dismissGlobally(Context context, List alarmIds) { - Set eventIds = new HashSet(alarmIds.size()); - for (AlarmId alarmId: alarmIds) { - eventIds.add(alarmId.mEventId); - } - } +class GlobalDismissManager : BroadcastReceiver() { + class AlarmId(var mEventId: Long, var mStart: Long) @Override @SuppressWarnings("unchecked") - public void onReceive(Context context, Intent intent) { - new AsyncTask, Void, Void>() { + override fun onReceive(context: Context?, intent: Intent?) { + object : AsyncTask?, Void?, Void?>() { @Override - protected Void doInBackground(Pair... params) { - return null; + protected override fun doInBackground(vararg params: Pair?): Void? { + return null + } + }.execute(Pair(context, intent)) + } + + companion object { + /** + * Globally dismiss notifications that are backed by the same events. + * + * @param context application context + * @param alarmIds Unique identifiers for events that have been dismissed by the user. + * @return true if notification_sender_id is available + */ + @JvmStatic fun dismissGlobally(context: Context?, alarmIds: List) { + val eventIds: HashSet = HashSet(alarmIds.size) + for (alarmId in alarmIds) { + eventIds.add(alarmId.mEventId) } - }.execute(new Pair(context, intent)); + } } -} +} \ No newline at end of file diff --git a/src/com/android/calendar/alerts/InitAlarmsService.kt b/src/com/android/calendar/alerts/InitAlarmsService.kt index 3a9b0b2c..0ac8a474 100644 --- a/src/com/android/calendar/alerts/InitAlarmsService.kt +++ b/src/com/android/calendar/alerts/InitAlarmsService.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,50 +13,50 @@ * See the License for the specific language governing permissions and * limitations under the License */ +package com.android.calendar.alerts -package com.android.calendar.alerts; - -import android.app.IntentService; -import android.content.ContentValues; -import android.content.Intent; -import android.net.Uri; -import android.os.SystemClock; -import android.provider.CalendarContract; -import android.util.Log; +import android.app.IntentService +import android.content.ContentValues +import android.content.Intent +import android.net.Uri +import android.os.SystemClock +import android.provider.CalendarContract +import android.util.Log /** * Service for clearing all scheduled alerts from the CalendarAlerts table and * rescheduling them. This is expected to be called only on boot up, to restore * the AlarmManager alarms that were lost on device restart. */ -public class InitAlarmsService extends IntentService { - private static final String TAG = "InitAlarmsService"; - private static final String SCHEDULE_ALARM_REMOVE_PATH = "schedule_alarms_remove"; - private static final Uri SCHEDULE_ALARM_REMOVE_URI = Uri.withAppendedPath( - CalendarContract.CONTENT_URI, SCHEDULE_ALARM_REMOVE_PATH); - - // Delay for rescheduling the alarms must be great enough to minimize race - // conditions with the provider's boot up actions. - private static final long DELAY_MS = 30000; - - public InitAlarmsService() { - super("InitAlarmsService"); - } - +class InitAlarmsService : IntentService("InitAlarmsService") { @Override - protected void onHandleIntent(Intent intent) { + protected override fun onHandleIntent(intent: Intent?) { // Delay to avoid race condition of in-progress alarm scheduling in provider. - SystemClock.sleep(DELAY_MS); - Log.d(TAG, "Clearing and rescheduling alarms."); + SystemClock.sleep(DELAY_MS) + Log.d(TAG, "Clearing and rescheduling alarms.") try { - getContentResolver().update(SCHEDULE_ALARM_REMOVE_URI, new ContentValues(), null, - null); - } catch (java.lang.IllegalArgumentException e) { + getContentResolver().update( + SCHEDULE_ALARM_REMOVE_URI, ContentValues(), null, + null + ) + } catch (e: java.lang.IllegalArgumentException) { // java.lang.IllegalArgumentException: // Unknown URI content://com.android.calendar/schedule_alarms_remove // Until b/7742576 is resolved, just catch the exception so the app won't crash - Log.e(TAG, "update failed: " + e.toString()); + Log.e(TAG, "update failed: " + e.toString()) } } -} + + companion object { + private const val TAG = "InitAlarmsService" + private const val SCHEDULE_ALARM_REMOVE_PATH = "schedule_alarms_remove" + private val SCHEDULE_ALARM_REMOVE_URI: Uri = Uri.withAppendedPath( + CalendarContract.CONTENT_URI, SCHEDULE_ALARM_REMOVE_PATH + ) + + // Delay for rescheduling the alarms must be great enough to minimize race + // conditions with the provider's boot up actions. + private const val DELAY_MS: Long = 30000 + } +} \ No newline at end of file -- cgit v1.2.3 From 3fd4ec0ddf8bd1f0cd81920558a785e568f06e81 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 17 Jun 2021 13:38:38 +0000 Subject: AOSP/Calendar - Copy of EventInfoActivity.java The Java code in EventInfoActivity.java has been copied into a corresponding .kt file. Test: manual - opening both .java and .kt files shows that they are identical. Change-Id: Ie5c7f2591c1cd5899ba83254b0c8f0ccb59a129c --- src/com/android/calendar/EventInfoActivity.kt | 190 ++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 src/com/android/calendar/EventInfoActivity.kt diff --git a/src/com/android/calendar/EventInfoActivity.kt b/src/com/android/calendar/EventInfoActivity.kt new file mode 100644 index 00000000..626e099d --- /dev/null +++ b/src/com/android/calendar/EventInfoActivity.kt @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2010 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 static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; +import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; +import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; + +import android.app.ActionBar; +import android.app.Activity; +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; + +import java.util.ArrayList; +import java.util.List; + +public class EventInfoActivity extends Activity { + private static final String TAG = "EventInfoActivity"; + private EventInfoFragment mInfoFragment; + 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); + + // Get the info needed for the fragment + Intent intent = getIntent(); + int attendeeResponse = 0; + mEventId = -1; + boolean isDialog = false; + + if (icicle != null) { + mEventId = icicle.getLong(EventInfoFragment.BUNDLE_KEY_EVENT_ID); + mStartMillis = icicle.getLong(EventInfoFragment.BUNDLE_KEY_START_MILLIS); + mEndMillis = icicle.getLong(EventInfoFragment.BUNDLE_KEY_END_MILLIS); + attendeeResponse = icicle.getInt(EventInfoFragment.BUNDLE_KEY_ATTENDEE_RESPONSE); + isDialog = icicle.getBoolean(EventInfoFragment.BUNDLE_KEY_IS_DIALOG); + } else if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { + mStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0); + mEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0); + attendeeResponse = intent.getIntExtra(ATTENDEE_STATUS, + Attendees.ATTENDEE_STATUS_NONE); + Uri data = intent.getData(); + if (data != null) { + try { + List pathSegments = data.getPathSegments(); + int size = pathSegments.size(); + if (size > 2 && "EventTime".equals(pathSegments.get(2))) { + // Support non-standard VIEW intent format: + //dat = content://com.android.calendar/events/[id]/EventTime/[start]/[end] + mEventId = Long.parseLong(pathSegments.get(1)); + if (size > 4) { + mStartMillis = Long.parseLong(pathSegments.get(3)); + mEndMillis = Long.parseLong(pathSegments.get(4)); + } + } else { + mEventId = Long.parseLong(data.getLastPathSegment()); + } + } catch (NumberFormatException e) { + if (mEventId == -1) { + // do nothing here , deal with it later + } else if (mStartMillis == 0 || mEndMillis ==0) { + // Parsing failed on the start or end time , make sure the times were not + // pulled from the intent's extras and reset them. + mStartMillis = 0; + mEndMillis = 0; + } + } + } + } + + if (mEventId == -1) { + Log.w(TAG, "No event id"); + Toast.makeText(this, R.string.event_not_found, Toast.LENGTH_SHORT).show(); + finish(); + } + + // If we do not support showing full screen event info in this configuration, + // close the activity and show the event in AllInOne. + Resources res = getResources(); + if (!res.getBoolean(R.bool.agenda_show_event_info_full_screen) + && !res.getBoolean(R.bool.show_event_info_full_screen)) { + CalendarController.getInstance(this) + .launchViewEvent(mEventId, mStartMillis, mEndMillis, attendeeResponse); + finish(); + return; + } + + setContentView(R.layout.simple_frame_layout); + + // Get the fragment if exists + mInfoFragment = (EventInfoFragment) + getFragmentManager().findFragmentById(R.id.main_frame); + + + // Remove the application title + ActionBar bar = getActionBar(); + if (bar != null) { + bar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME); + } + + // Create a new fragment if none exists + if (mInfoFragment == null) { + FragmentManager fragmentManager = getFragmentManager(); + FragmentTransaction ft = fragmentManager.beginTransaction(); + mInfoFragment = new EventInfoFragment(this, mEventId, mStartMillis, mEndMillis, + attendeeResponse, isDialog, (isDialog ? + EventInfoFragment.DIALOG_WINDOW_STYLE : + EventInfoFragment.FULL_WINDOW_STYLE)); + ft.replace(R.id.main_frame, mInfoFragment); + ft.commit(); + } + } + + @Override + protected void onNewIntent(Intent intent) { + // From the Android Dev Guide: "It's important to note that when + // onNewIntent(Intent) is called, the Activity has not been restarted, + // so the getIntent() method will still return the Intent that was first + // received with onCreate(). This is why setIntent(Intent) is called + // inside onNewIntent(Intent) (just in case you call getIntent() at a + // later time)." + setIntent(intent); + } + + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + } + + @Override + protected void onResume() { + super.onResume(); + getContentResolver().registerContentObserver(CalendarContract.Events.CONTENT_URI, + true, mObserver); + } + + @Override + protected void onPause() { + super.onPause(); + getContentResolver().unregisterContentObserver(mObserver); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + } +} -- cgit v1.2.3 From b813f3a6db1132f2ce5b144db7b30651905ce741 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 17 Jun 2021 15:20:45 +0000 Subject: AOSP/Calendar - EventInfoActivity fully converted with bp file Using Android Studio, EventInfoActivty was converted to Kotlin. Intially the converter would set certain primitive types, such as long types, to be nullable in Kotlin (Long?) when in realilty this just caused various compile errors. This was corrected by changing the Kotlin types to only be nullable when necessary and through casting. Another issue had to do with nullable object references invoking functions without null safety. This also had to be manually implemented. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.......... com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=........ Time: 0.192 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 8m 7s Total aggregated tests run time: 8m 7s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.77 tests/sec [376 tests / 487482 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 8975 ms || clean = 1763 ms Total preparation time: 8s || Total tear down time: 1s ======================================================= =============== Summary =============== Total Run time: 9m 45s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: Iaeac5c6511ee8cd50b83f4c7d3819fbfbb2cff51 --- Android.bp | 1 + src/com/android/calendar/EventInfoActivity.kt | 224 +++++++++++++------------- 2 files changed, 116 insertions(+), 109 deletions(-) diff --git a/Android.bp b/Android.bp index dafa3c63..33cd6a91 100644 --- a/Android.bp +++ b/Android.bp @@ -28,6 +28,7 @@ exclude_srcsd = [ "src/**/calendar/alerts/NotificationMgr.java", "src/**/calendar/alerts/QuickResponseActivity.java", "src/**/calendar/DayOfMonthDrawable.java", + "src/**/calendar/EventInfoActivity.java", ] exclude_srcsm = [ diff --git a/src/com/android/calendar/EventInfoActivity.kt b/src/com/android/calendar/EventInfoActivity.kt index 626e099d..8c15b992 100644 --- a/src/com/android/calendar/EventInfoActivity.kt +++ b/src/com/android/calendar/EventInfoActivity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,178 +13,184 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.calendar; - -import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; -import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; -import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; - -import android.app.ActionBar; -import android.app.Activity; -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; - -import java.util.ArrayList; -import java.util.List; - -public class EventInfoActivity extends Activity { - private static final String TAG = "EventInfoActivity"; - private EventInfoFragment mInfoFragment; - private long mStartMillis, mEndMillis; - private long mEventId; +package com.android.calendar + +import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME +import android.provider.CalendarContract.EXTRA_EVENT_END_TIME +import android.provider.CalendarContract.Attendees.ATTENDEE_STATUS +import android.app.ActionBar +import android.app.Activity +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 + +class EventInfoActivity : Activity() { + private var mInfoFragment: EventInfoFragment? = null + private var mStartMillis: Long = 0 + private var mEndMillis: Long = 0 + private var mEventId: Long = 0 // Create an observer so that we can update the views whenever a // Calendar event changes. - private final ContentObserver mObserver = new ContentObserver(new Handler()) { + private val mObserver: ContentObserver = object : ContentObserver(Handler()) { @Override - public boolean deliverSelfNotifications() { - return false; + override fun deliverSelfNotifications(): Boolean { + return false } @Override - public void onChange(boolean selfChange) { - if (selfChange) return; - if (mInfoFragment != null) { - mInfoFragment.reloadEvents(); + override fun onChange(selfChange: Boolean) { + if (selfChange) return + val temp = mInfoFragment + if (temp != null) { + mInfoFragment?.reloadEvents() } } - }; + } @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); + protected override fun onCreate(icicle: Bundle?) { + super.onCreate(icicle) // Get the info needed for the fragment - Intent intent = getIntent(); - int attendeeResponse = 0; - mEventId = -1; - boolean isDialog = false; - + val intent: Intent = getIntent() + var attendeeResponse = 0 + mEventId = -1 + var isDialog = false if (icicle != null) { - mEventId = icicle.getLong(EventInfoFragment.BUNDLE_KEY_EVENT_ID); - mStartMillis = icicle.getLong(EventInfoFragment.BUNDLE_KEY_START_MILLIS); - mEndMillis = icicle.getLong(EventInfoFragment.BUNDLE_KEY_END_MILLIS); - attendeeResponse = icicle.getInt(EventInfoFragment.BUNDLE_KEY_ATTENDEE_RESPONSE); - isDialog = icicle.getBoolean(EventInfoFragment.BUNDLE_KEY_IS_DIALOG); + mEventId = icicle.getLong(EventInfoFragment.BUNDLE_KEY_EVENT_ID) + mStartMillis = icicle.getLong(EventInfoFragment.BUNDLE_KEY_START_MILLIS) + mEndMillis = icicle.getLong(EventInfoFragment.BUNDLE_KEY_END_MILLIS) + attendeeResponse = icicle.getInt(EventInfoFragment.BUNDLE_KEY_ATTENDEE_RESPONSE) + isDialog = icicle.getBoolean(EventInfoFragment.BUNDLE_KEY_IS_DIALOG) } else if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { - mStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0); - mEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0); - attendeeResponse = intent.getIntExtra(ATTENDEE_STATUS, - Attendees.ATTENDEE_STATUS_NONE); - Uri data = intent.getData(); + mStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0) + mEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0) + attendeeResponse = intent.getIntExtra( + ATTENDEE_STATUS, + Attendees.ATTENDEE_STATUS_NONE + ) + val data: Uri? = intent.getData() if (data != null) { try { - List pathSegments = data.getPathSegments(); - int size = pathSegments.size(); - if (size > 2 && "EventTime".equals(pathSegments.get(2))) { + val pathSegments = data.getPathSegments() + val size: Int = pathSegments.size + if (size > 2 && "EventTime".equals(pathSegments[2])) { // Support non-standard VIEW intent format: - //dat = content://com.android.calendar/events/[id]/EventTime/[start]/[end] - mEventId = Long.parseLong(pathSegments.get(1)); + // dat = content://com.android.calendar/events/[id]/EventTime/[start]/[end] + mEventId = pathSegments[1].toLong() if (size > 4) { - mStartMillis = Long.parseLong(pathSegments.get(3)); - mEndMillis = Long.parseLong(pathSegments.get(4)); + mStartMillis = pathSegments[3].toLong() + mEndMillis = pathSegments[4].toLong() } } else { - mEventId = Long.parseLong(data.getLastPathSegment()); + mEventId = data.getLastPathSegment() as Long } - } catch (NumberFormatException e) { - if (mEventId == -1) { + } catch (e: NumberFormatException) { + if (mEventId == -1L) { // do nothing here , deal with it later - } else if (mStartMillis == 0 || mEndMillis ==0) { + } else if (mStartMillis == 0L || mEndMillis == 0L) { // Parsing failed on the start or end time , make sure the times were not // pulled from the intent's extras and reset them. - mStartMillis = 0; - mEndMillis = 0; + mStartMillis = 0 + mEndMillis = 0 } } } } - - if (mEventId == -1) { - Log.w(TAG, "No event id"); - Toast.makeText(this, R.string.event_not_found, Toast.LENGTH_SHORT).show(); - finish(); + if (mEventId == -1L) { + Log.w(TAG, "No event id") + Toast.makeText(this, R.string.event_not_found, Toast.LENGTH_SHORT).show() + finish() } // If we do not support showing full screen event info in this configuration, // close the activity and show the event in AllInOne. - Resources res = getResources(); - if (!res.getBoolean(R.bool.agenda_show_event_info_full_screen) - && !res.getBoolean(R.bool.show_event_info_full_screen)) { + val res: Resources = getResources() + if (!res.getBoolean(R.bool.agenda_show_event_info_full_screen) && + !res.getBoolean(R.bool.show_event_info_full_screen) + ) { CalendarController.getInstance(this) - .launchViewEvent(mEventId, mStartMillis, mEndMillis, attendeeResponse); - finish(); - return; + .launchViewEvent(mEventId, mStartMillis, mEndMillis, attendeeResponse) + finish() + return } - - setContentView(R.layout.simple_frame_layout); + setContentView(R.layout.simple_frame_layout) // Get the fragment if exists - mInfoFragment = (EventInfoFragment) - getFragmentManager().findFragmentById(R.id.main_frame); - + mInfoFragment = getFragmentManager().findFragmentById(R.id.main_frame) as EventInfoFragment // Remove the application title - ActionBar bar = getActionBar(); + val bar: ActionBar? = getActionBar() if (bar != null) { - bar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME); + bar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP or ActionBar.DISPLAY_SHOW_HOME) } // Create a new fragment if none exists if (mInfoFragment == null) { - FragmentManager fragmentManager = getFragmentManager(); - FragmentTransaction ft = fragmentManager.beginTransaction(); - mInfoFragment = new EventInfoFragment(this, mEventId, mStartMillis, mEndMillis, - attendeeResponse, isDialog, (isDialog ? - EventInfoFragment.DIALOG_WINDOW_STYLE : - EventInfoFragment.FULL_WINDOW_STYLE)); - ft.replace(R.id.main_frame, mInfoFragment); - ft.commit(); + val fragmentManager: FragmentManager = getFragmentManager() + val ft: FragmentTransaction = fragmentManager.beginTransaction() + mInfoFragment = EventInfoFragment( + this, + mEventId, + mStartMillis, + mEndMillis, + attendeeResponse, + isDialog, + if (isDialog) EventInfoFragment.DIALOG_WINDOW_STYLE + else EventInfoFragment.FULL_WINDOW_STYLE + ) + ft.replace(R.id.main_frame, mInfoFragment) + ft.commit() } } @Override - protected void onNewIntent(Intent intent) { + protected override fun onNewIntent(intent: Intent?) { // From the Android Dev Guide: "It's important to note that when // onNewIntent(Intent) is called, the Activity has not been restarted, // so the getIntent() method will still return the Intent that was first // received with onCreate(). This is why setIntent(Intent) is called // inside onNewIntent(Intent) (just in case you call getIntent() at a // later time)." - setIntent(intent); + setIntent(intent) } - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) } @Override - protected void onResume() { - super.onResume(); - getContentResolver().registerContentObserver(CalendarContract.Events.CONTENT_URI, - true, mObserver); + protected override fun onResume() { + super.onResume() + getContentResolver().registerContentObserver( + CalendarContract.Events.CONTENT_URI, + true, mObserver + ) } @Override - protected void onPause() { - super.onPause(); - getContentResolver().unregisterContentObserver(mObserver); + protected override fun onPause() { + super.onPause() + getContentResolver().unregisterContentObserver(mObserver) } @Override - protected void onDestroy() { - super.onDestroy(); + protected override fun onDestroy() { + super.onDestroy() + } + + companion object { + private const val TAG = "EventInfoActivity" } -} +} \ No newline at end of file -- cgit v1.2.3 From c77f767cf03ad73386b75f52120d60ca8ba14111 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Thu, 17 Jun 2021 16:56:25 +0000 Subject: AOSP/Calendar - Add Kotlin copy of OtherPreferences.java Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: I8f13769b571e9422fb05fc9d90985ecd1c74adf0 --- src/com/android/calendar/OtherPreferences.kt | 210 +++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 src/com/android/calendar/OtherPreferences.kt diff --git a/src/com/android/calendar/OtherPreferences.kt b/src/com/android/calendar/OtherPreferences.kt new file mode 100644 index 00000000..a59d3f46 --- /dev/null +++ b/src/com/android/calendar/OtherPreferences.kt @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2011 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.app.Activity; +import android.app.Dialog; +import android.app.TimePickerDialog; +import android.content.ComponentName; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; +import android.text.format.DateFormat; +import android.text.format.Time; +import android.util.Log; +import android.widget.TimePicker; + +public class OtherPreferences extends PreferenceFragment implements OnPreferenceChangeListener{ + private static final String TAG = "CalendarOtherPreferences"; + + // The name of the shared preferences file. This name must be maintained for + // historical reasons, as it's what PreferenceManager assigned the first + // time the file was created. + static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; + + // Must be the same keys that are used in the other_preferences.xml file. + public static final String KEY_OTHER_COPY_DB = "preferences_copy_db"; + public static final String KEY_OTHER_QUIET_HOURS = "preferences_reminders_quiet_hours"; + public static final String KEY_OTHER_REMINDERS_RESPONDED = "preferences_reminders_responded"; + public static final String KEY_OTHER_QUIET_HOURS_START = + "preferences_reminders_quiet_hours_start"; + public static final String KEY_OTHER_QUIET_HOURS_START_HOUR = + "preferences_reminders_quiet_hours_start_hour"; + public static final String KEY_OTHER_QUIET_HOURS_START_MINUTE = + "preferences_reminders_quiet_hours_start_minute"; + public static final String KEY_OTHER_QUIET_HOURS_END = + "preferences_reminders_quiet_hours_end"; + public static final String KEY_OTHER_QUIET_HOURS_END_HOUR = + "preferences_reminders_quiet_hours_end_hour"; + public static final String KEY_OTHER_QUIET_HOURS_END_MINUTE = + "preferences_reminders_quiet_hours_end_minute"; + public static final String KEY_OTHER_1 = "preferences_tardis_1"; + + public static final int QUIET_HOURS_DEFAULT_START_HOUR = 22; + public static final int QUIET_HOURS_DEFAULT_START_MINUTE = 0; + public static final int QUIET_HOURS_DEFAULT_END_HOUR = 8; + public static final int QUIET_HOURS_DEFAULT_END_MINUTE = 0; + + private static final int START_LISTENER = 1; + private static final int END_LISTENER = 2; + private static final String format24Hour = "%H:%M"; + private static final String format12Hour = "%I:%M%P"; + + private Preference mCopyDb; + private CheckBoxPreference mQuietHours; + private Preference mQuietHoursStart; + private Preference mQuietHoursEnd; + + private TimePickerDialog mTimePickerDialog; + private TimeSetListener mQuietHoursStartListener; + private TimePickerDialog mQuietHoursStartDialog; + private TimeSetListener mQuietHoursEndListener; + private TimePickerDialog mQuietHoursEndDialog; + private boolean mIs24HourMode; + + public OtherPreferences() { + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + PreferenceManager manager = getPreferenceManager(); + manager.setSharedPreferencesName(SHARED_PREFS_NAME); + SharedPreferences prefs = manager.getSharedPreferences(); + + addPreferencesFromResource(R.xml.other_preferences); + mCopyDb = findPreference(KEY_OTHER_COPY_DB); + + Activity activity = getActivity(); + if (activity == null) { + Log.d(TAG, "Activity was null"); + } + mIs24HourMode = DateFormat.is24HourFormat(activity); + + mQuietHours = + (CheckBoxPreference) findPreference(KEY_OTHER_QUIET_HOURS); + + int startHour = prefs.getInt(KEY_OTHER_QUIET_HOURS_START_HOUR, + QUIET_HOURS_DEFAULT_START_HOUR); + int startMinute = prefs.getInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, + QUIET_HOURS_DEFAULT_START_MINUTE); + mQuietHoursStart = findPreference(KEY_OTHER_QUIET_HOURS_START); + mQuietHoursStartListener = new TimeSetListener(START_LISTENER); + mQuietHoursStartDialog = new TimePickerDialog( + activity, mQuietHoursStartListener, + startHour, startMinute, mIs24HourMode); + mQuietHoursStart.setSummary(formatTime(startHour, startMinute)); + + int endHour = prefs.getInt(KEY_OTHER_QUIET_HOURS_END_HOUR, + QUIET_HOURS_DEFAULT_END_HOUR); + int endMinute = prefs.getInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, + QUIET_HOURS_DEFAULT_END_MINUTE); + mQuietHoursEnd = findPreference(KEY_OTHER_QUIET_HOURS_END); + mQuietHoursEndListener = new TimeSetListener(END_LISTENER); + mQuietHoursEndDialog = new TimePickerDialog( + activity, mQuietHoursEndListener, + endHour, endMinute, mIs24HourMode); + mQuietHoursEnd.setSummary(formatTime(endHour, endMinute)); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object objValue) { + return true; + } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { + if (preference == mCopyDb) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setComponent(new ComponentName("com.android.providers.calendar", + "com.android.providers.calendar.CalendarDebugActivity")); + startActivity(intent); + } else if (preference == mQuietHoursStart) { + if (mTimePickerDialog == null) { + mTimePickerDialog = mQuietHoursStartDialog; + mTimePickerDialog.show(); + } else { + Log.v(TAG, "not null"); + } + } else if (preference == mQuietHoursEnd) { + if (mTimePickerDialog == null) { + mTimePickerDialog = mQuietHoursEndDialog; + mTimePickerDialog.show(); + } else { + Log.v(TAG, "not null"); + } + } else { + return super.onPreferenceTreeClick(screen, preference); + } + return true; + } + + private class TimeSetListener implements TimePickerDialog.OnTimeSetListener { + private int mListenerId; + + public TimeSetListener(int listenerId) { + mListenerId = listenerId; + } + + @Override + public void onTimeSet(TimePicker view, int hourOfDay, int minute) { + mTimePickerDialog = null; + + SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); + SharedPreferences.Editor editor = prefs.edit(); + + String summary = formatTime(hourOfDay, minute); + switch (mListenerId) { + case (START_LISTENER): + mQuietHoursStart.setSummary(summary); + editor.putInt(KEY_OTHER_QUIET_HOURS_START_HOUR, hourOfDay); + editor.putInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, minute); + break; + case (END_LISTENER): + mQuietHoursEnd.setSummary(summary); + editor.putInt(KEY_OTHER_QUIET_HOURS_END_HOUR, hourOfDay); + editor.putInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, minute); + break; + default: + Log.d(TAG, "Set time for unknown listener: "+mListenerId); + } + + editor.commit(); + } + } + + /** + * @param hourOfDay the hour of the day (0-24) + * @param minute + * @return human-readable string formatted based on 24-hour mode. + */ + private String formatTime(int hourOfDay, int minute) { + Time time = new Time(); + time.hour = hourOfDay; + time.minute = minute; + + String format = mIs24HourMode? format24Hour : format12Hour; + return time.format(format); + } +} -- cgit v1.2.3 From dbf48a8a5025f3e141b1cf68b03143a716ebc394 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Thu, 17 Jun 2021 17:45:33 +0000 Subject: AOSP/Calendar - Add Kotlin code for OtherPreferences.kt. Also uploaded corresponding Android.bp file There were no major changes in this file. I added a few safe casting "?" to potentially null variables. Override functions flags must be manually included in function declarations. Test: manual (ran build and unit tests) and CTS testing suite $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.182 OK (22 tests) CTS TESTING: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ============================================ ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 27m 0s Total aggregated tests run time: 27m 0s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.23 tests/sec [376 tests / 1620646 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 11813 ms || clean = 2703 ms Total preparation time: 11s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 29m 15s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ BUG: 189873985 Change-Id: Id3f432a9ee2f621491a3940eb8b7fc598cb11b81 --- Android.bp | 1 + src/com/android/calendar/OtherPreferences.kt | 298 ++++++++++++--------------- 2 files changed, 137 insertions(+), 162 deletions(-) diff --git a/Android.bp b/Android.bp index dafa3c63..6e219d92 100644 --- a/Android.bp +++ b/Android.bp @@ -38,6 +38,7 @@ exclude_srcsm = [ "src/**/calendar/EventGeometry.java", "src/**/calendar/GoogleCalendarUriIntentFilter.java", "src/**/calendar/MultiStateButton.java", + "src/**/calendar/OtherPreferences.java", "src/**/calendar/UpgradeReceiver.java", ] diff --git a/src/com/android/calendar/OtherPreferences.kt b/src/com/android/calendar/OtherPreferences.kt index a59d3f46..f1507ccf 100644 --- a/src/com/android/calendar/OtherPreferences.kt +++ b/src/com/android/calendar/OtherPreferences.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,184 +13,129 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar; - -import android.app.Activity; -import android.app.Dialog; -import android.app.TimePickerDialog; -import android.content.ComponentName; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.text.format.DateFormat; -import android.text.format.Time; -import android.util.Log; -import android.widget.TimePicker; - -public class OtherPreferences extends PreferenceFragment implements OnPreferenceChangeListener{ - private static final String TAG = "CalendarOtherPreferences"; - - // The name of the shared preferences file. This name must be maintained for - // historical reasons, as it's what PreferenceManager assigned the first - // time the file was created. - static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; - - // Must be the same keys that are used in the other_preferences.xml file. - public static final String KEY_OTHER_COPY_DB = "preferences_copy_db"; - public static final String KEY_OTHER_QUIET_HOURS = "preferences_reminders_quiet_hours"; - public static final String KEY_OTHER_REMINDERS_RESPONDED = "preferences_reminders_responded"; - public static final String KEY_OTHER_QUIET_HOURS_START = - "preferences_reminders_quiet_hours_start"; - public static final String KEY_OTHER_QUIET_HOURS_START_HOUR = - "preferences_reminders_quiet_hours_start_hour"; - public static final String KEY_OTHER_QUIET_HOURS_START_MINUTE = - "preferences_reminders_quiet_hours_start_minute"; - public static final String KEY_OTHER_QUIET_HOURS_END = - "preferences_reminders_quiet_hours_end"; - public static final String KEY_OTHER_QUIET_HOURS_END_HOUR = - "preferences_reminders_quiet_hours_end_hour"; - public static final String KEY_OTHER_QUIET_HOURS_END_MINUTE = - "preferences_reminders_quiet_hours_end_minute"; - public static final String KEY_OTHER_1 = "preferences_tardis_1"; - - public static final int QUIET_HOURS_DEFAULT_START_HOUR = 22; - public static final int QUIET_HOURS_DEFAULT_START_MINUTE = 0; - public static final int QUIET_HOURS_DEFAULT_END_HOUR = 8; - public static final int QUIET_HOURS_DEFAULT_END_MINUTE = 0; - - private static final int START_LISTENER = 1; - private static final int END_LISTENER = 2; - private static final String format24Hour = "%H:%M"; - private static final String format12Hour = "%I:%M%P"; - - private Preference mCopyDb; - private CheckBoxPreference mQuietHours; - private Preference mQuietHoursStart; - private Preference mQuietHoursEnd; - - private TimePickerDialog mTimePickerDialog; - private TimeSetListener mQuietHoursStartListener; - private TimePickerDialog mQuietHoursStartDialog; - private TimeSetListener mQuietHoursEndListener; - private TimePickerDialog mQuietHoursEndDialog; - private boolean mIs24HourMode; - - public OtherPreferences() { - } +package com.android.calendar + +import android.app.Activity +import android.app.Dialog +import android.app.TimePickerDialog +import android.content.ComponentName +import android.content.Intent +import android.content.SharedPreferences +import android.os.Bundle +import android.preference.CheckBoxPreference +import android.preference.ListPreference +import android.preference.Preference +import android.preference.Preference.OnPreferenceChangeListener +import android.preference.PreferenceFragment +import android.preference.PreferenceManager +import android.preference.PreferenceScreen +import android.text.format.DateFormat +import android.text.format.Time +import android.util.Log +import android.widget.TimePicker + +class OtherPreferences : PreferenceFragment(), OnPreferenceChangeListener { + private var mCopyDb: Preference? = null + private var mQuietHours: CheckBoxPreference? = null + private var mQuietHoursStart: Preference? = null + private var mQuietHoursEnd: Preference? = null + private var mTimePickerDialog: TimePickerDialog? = null + private var mQuietHoursStartListener: TimeSetListener? = null + private var mQuietHoursStartDialog: TimePickerDialog? = null + private var mQuietHoursEndListener: TimeSetListener? = null + private var mQuietHoursEndDialog: TimePickerDialog? = null + private var mIs24HourMode = false @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - PreferenceManager manager = getPreferenceManager(); - manager.setSharedPreferencesName(SHARED_PREFS_NAME); - SharedPreferences prefs = manager.getSharedPreferences(); - - addPreferencesFromResource(R.xml.other_preferences); - mCopyDb = findPreference(KEY_OTHER_COPY_DB); - - Activity activity = getActivity(); + override fun onCreate(icicle: Bundle?) { + super.onCreate(icicle) + val manager: PreferenceManager = getPreferenceManager() + manager.setSharedPreferencesName(SHARED_PREFS_NAME) + val prefs: SharedPreferences = manager.getSharedPreferences() + addPreferencesFromResource(R.xml.other_preferences) + mCopyDb = findPreference(KEY_OTHER_COPY_DB) + val activity: Activity = getActivity() if (activity == null) { - Log.d(TAG, "Activity was null"); + Log.d(TAG, "Activity was null") } - mIs24HourMode = DateFormat.is24HourFormat(activity); - - mQuietHours = - (CheckBoxPreference) findPreference(KEY_OTHER_QUIET_HOURS); - - int startHour = prefs.getInt(KEY_OTHER_QUIET_HOURS_START_HOUR, - QUIET_HOURS_DEFAULT_START_HOUR); - int startMinute = prefs.getInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, - QUIET_HOURS_DEFAULT_START_MINUTE); - mQuietHoursStart = findPreference(KEY_OTHER_QUIET_HOURS_START); - mQuietHoursStartListener = new TimeSetListener(START_LISTENER); - mQuietHoursStartDialog = new TimePickerDialog( + mIs24HourMode = DateFormat.is24HourFormat(activity) + mQuietHours = findPreference(KEY_OTHER_QUIET_HOURS) as CheckBoxPreference? + val startHour: Int = prefs.getInt(KEY_OTHER_QUIET_HOURS_START_HOUR, + QUIET_HOURS_DEFAULT_START_HOUR) + val startMinute: Int = prefs.getInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, + QUIET_HOURS_DEFAULT_START_MINUTE) + mQuietHoursStart = findPreference(KEY_OTHER_QUIET_HOURS_START) + mQuietHoursStartListener = TimeSetListener(START_LISTENER) + mQuietHoursStartDialog = TimePickerDialog( activity, mQuietHoursStartListener, - startHour, startMinute, mIs24HourMode); - mQuietHoursStart.setSummary(formatTime(startHour, startMinute)); - - int endHour = prefs.getInt(KEY_OTHER_QUIET_HOURS_END_HOUR, - QUIET_HOURS_DEFAULT_END_HOUR); - int endMinute = prefs.getInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, - QUIET_HOURS_DEFAULT_END_MINUTE); - mQuietHoursEnd = findPreference(KEY_OTHER_QUIET_HOURS_END); - mQuietHoursEndListener = new TimeSetListener(END_LISTENER); - mQuietHoursEndDialog = new TimePickerDialog( + startHour, startMinute, mIs24HourMode) + mQuietHoursStart?.setSummary(formatTime(startHour, startMinute)) + val endHour: Int = prefs.getInt(KEY_OTHER_QUIET_HOURS_END_HOUR, + QUIET_HOURS_DEFAULT_END_HOUR) + val endMinute: Int = prefs.getInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, + QUIET_HOURS_DEFAULT_END_MINUTE) + mQuietHoursEnd = findPreference(KEY_OTHER_QUIET_HOURS_END) + mQuietHoursEndListener = TimeSetListener(END_LISTENER) + mQuietHoursEndDialog = TimePickerDialog( activity, mQuietHoursEndListener, - endHour, endMinute, mIs24HourMode); - mQuietHoursEnd.setSummary(formatTime(endHour, endMinute)); + endHour, endMinute, mIs24HourMode) + mQuietHoursEnd?.setSummary(formatTime(endHour, endMinute)) } @Override - public boolean onPreferenceChange(Preference preference, Object objValue) { - return true; + override fun onPreferenceChange(preference: Preference?, objValue: Any?): Boolean { + return true } @Override - public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { - if (preference == mCopyDb) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setComponent(new ComponentName("com.android.providers.calendar", - "com.android.providers.calendar.CalendarDebugActivity")); - startActivity(intent); - } else if (preference == mQuietHoursStart) { + override fun onPreferenceTreeClick(screen: PreferenceScreen?, preference: Preference): Boolean { + if (preference === mCopyDb) { + val intent = Intent(Intent.ACTION_MAIN) + intent.setComponent(ComponentName("com.android.providers.calendar", + "com.android.providers.calendar.CalendarDebugActivity")) + startActivity(intent) + } else if (preference === mQuietHoursStart) { if (mTimePickerDialog == null) { - mTimePickerDialog = mQuietHoursStartDialog; - mTimePickerDialog.show(); + mTimePickerDialog = mQuietHoursStartDialog + mTimePickerDialog?.show() } else { - Log.v(TAG, "not null"); + Log.v(TAG, "not null") } - } else if (preference == mQuietHoursEnd) { + } else if (preference === mQuietHoursEnd) { if (mTimePickerDialog == null) { - mTimePickerDialog = mQuietHoursEndDialog; - mTimePickerDialog.show(); + mTimePickerDialog = mQuietHoursEndDialog + mTimePickerDialog?.show() } else { - Log.v(TAG, "not null"); + Log.v(TAG, "not null") } } else { - return super.onPreferenceTreeClick(screen, preference); + return super.onPreferenceTreeClick(screen, preference) } - return true; + return true } - private class TimeSetListener implements TimePickerDialog.OnTimeSetListener { - private int mListenerId; - - public TimeSetListener(int listenerId) { - mListenerId = listenerId; - } - + private inner class TimeSetListener(private val mListenerId: Int) : + TimePickerDialog.OnTimeSetListener { @Override - public void onTimeSet(TimePicker view, int hourOfDay, int minute) { - mTimePickerDialog = null; - - SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); - SharedPreferences.Editor editor = prefs.edit(); - - String summary = formatTime(hourOfDay, minute); - switch (mListenerId) { - case (START_LISTENER): - mQuietHoursStart.setSummary(summary); - editor.putInt(KEY_OTHER_QUIET_HOURS_START_HOUR, hourOfDay); - editor.putInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, minute); - break; - case (END_LISTENER): - mQuietHoursEnd.setSummary(summary); - editor.putInt(KEY_OTHER_QUIET_HOURS_END_HOUR, hourOfDay); - editor.putInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, minute); - break; - default: - Log.d(TAG, "Set time for unknown listener: "+mListenerId); + override fun onTimeSet(view: TimePicker?, hourOfDay: Int, minute: Int) { + mTimePickerDialog = null + val prefs: SharedPreferences = getPreferenceManager().getSharedPreferences() + val editor: SharedPreferences.Editor = prefs.edit() + val summary = formatTime(hourOfDay, minute) + when (mListenerId) { + START_LISTENER -> { + mQuietHoursStart?.setSummary(summary) + editor.putInt(KEY_OTHER_QUIET_HOURS_START_HOUR, hourOfDay) + editor.putInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, minute) + } + END_LISTENER -> { + mQuietHoursEnd?.setSummary(summary) + editor.putInt(KEY_OTHER_QUIET_HOURS_END_HOUR, hourOfDay) + editor.putInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, minute) + } + else -> Log.d(TAG, "Set time for unknown listener: $mListenerId") } - - editor.commit(); + editor.commit() } } @@ -199,12 +144,41 @@ public class OtherPreferences extends PreferenceFragment implements OnPreferenc * @param minute * @return human-readable string formatted based on 24-hour mode. */ - private String formatTime(int hourOfDay, int minute) { - Time time = new Time(); - time.hour = hourOfDay; - time.minute = minute; + private fun formatTime(hourOfDay: Int, minute: Int): String { + val time = Time() + time.hour = hourOfDay + time.minute = minute + val format = if (mIs24HourMode) format24Hour else format12Hour + return time.format(format) + } - String format = mIs24HourMode? format24Hour : format12Hour; - return time.format(format); + companion object { + private const val TAG = "CalendarOtherPreferences" + + // The name of the shared preferences file. This name must be maintained for + // historical reasons, as it's what PreferenceManager assigned the first + // time the file was created. + const val SHARED_PREFS_NAME = "com.android.calendar_preferences" + + // Must be the same keys that are used in the other_preferences.xml file. + const val KEY_OTHER_COPY_DB = "preferences_copy_db" + const val KEY_OTHER_QUIET_HOURS = "preferences_reminders_quiet_hours" + const val KEY_OTHER_REMINDERS_RESPONDED = "preferences_reminders_responded" + const val KEY_OTHER_QUIET_HOURS_START = "preferences_reminders_quiet_hours_start" + const val KEY_OTHER_QUIET_HOURS_START_HOUR = "preferences_reminders_quiet_hours_start_hour" + const val KEY_OTHER_QUIET_HOURS_START_MINUTE = + "preferences_reminders_quiet_hours_start_minute" + const val KEY_OTHER_QUIET_HOURS_END = "preferences_reminders_quiet_hours_end" + const val KEY_OTHER_QUIET_HOURS_END_HOUR = "preferences_reminders_quiet_hours_end_hour" + const val KEY_OTHER_QUIET_HOURS_END_MINUTE = "preferences_reminders_quiet_hours_end_minute" + const val KEY_OTHER_1 = "preferences_tardis_1" + const val QUIET_HOURS_DEFAULT_START_HOUR = 22 + const val QUIET_HOURS_DEFAULT_START_MINUTE = 0 + const val QUIET_HOURS_DEFAULT_END_HOUR = 8 + const val QUIET_HOURS_DEFAULT_END_MINUTE = 0 + private const val START_LISTENER = 1 + private const val END_LISTENER = 2 + private const val format24Hour = "%H:%M" + private const val format12Hour = "%I:%M%P" } -} +} \ No newline at end of file -- cgit v1.2.3 From df7d9cc2bbef3cb571ea2a933e0bda17a05ba238 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Thu, 17 Jun 2021 22:32:10 +0000 Subject: AOSP/Calendar - Add Kotlin copy of GeneralPreferences.java Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: Ieb321a0888383ac1308b67fef3575e602528275f --- src/com/android/calendar/GeneralPreferences.kt | 400 +++++++++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 src/com/android/calendar/GeneralPreferences.kt diff --git a/src/com/android/calendar/GeneralPreferences.kt b/src/com/android/calendar/GeneralPreferences.kt new file mode 100644 index 00000000..a42f07e3 --- /dev/null +++ b/src/com/android/calendar/GeneralPreferences.kt @@ -0,0 +1,400 @@ +/* + * 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.app.Activity; +import android.app.FragmentManager; +import android.app.backup.BackupManager; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.Vibrator; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceCategory; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; +import android.preference.RingtonePreference; +import android.provider.CalendarContract; +import android.provider.CalendarContract.CalendarCache; +import android.provider.SearchRecentSuggestions; +import android.text.TextUtils; +import android.text.format.Time; +import android.widget.Toast; + +import com.android.calendar.alerts.AlertReceiver; +import com.android.timezonepicker.TimeZoneInfo; +import com.android.timezonepicker.TimeZonePickerDialog; +import com.android.timezonepicker.TimeZonePickerDialog.OnTimeZoneSetListener; +import com.android.timezonepicker.TimeZonePickerUtils; + +public class GeneralPreferences extends PreferenceFragment implements + OnSharedPreferenceChangeListener, OnPreferenceChangeListener, OnTimeZoneSetListener { + // The name of the shared preferences file. This name must be maintained for historical + // reasons, as it's what PreferenceManager assigned the first time the file was created. + static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; + static final String SHARED_PREFS_NAME_NO_BACKUP = "com.android.calendar_preferences_no_backup"; + + private static final String FRAG_TAG_TIME_ZONE_PICKER = "TimeZonePicker"; + + // Preference keys + public static final String KEY_HIDE_DECLINED = "preferences_hide_declined"; + public static final String KEY_WEEK_START_DAY = "preferences_week_start_day"; + public static final String KEY_SHOW_WEEK_NUM = "preferences_show_week_num"; + public static final String KEY_DAYS_PER_WEEK = "preferences_days_per_week"; + public static final String KEY_SKIP_SETUP = "preferences_skip_setup"; + + public static final String KEY_CLEAR_SEARCH_HISTORY = "preferences_clear_search_history"; + + public static final String KEY_ALERTS_CATEGORY = "preferences_alerts_category"; + public static final String KEY_ALERTS = "preferences_alerts"; + public static final String KEY_ALERTS_VIBRATE = "preferences_alerts_vibrate"; + public static final String KEY_ALERTS_RINGTONE = "preferences_alerts_ringtone"; + public static final String KEY_ALERTS_POPUP = "preferences_alerts_popup"; + + public static final String KEY_SHOW_CONTROLS = "preferences_show_controls"; + + public static final String KEY_DEFAULT_REMINDER = "preferences_default_reminder"; + public static final int NO_REMINDER = -1; + public static final String NO_REMINDER_STRING = "-1"; + public static final int REMINDER_DEFAULT_TIME = 10; // in minutes + + public static final String KEY_DEFAULT_CELL_HEIGHT = "preferences_default_cell_height"; + public static final String KEY_VERSION = "preferences_version"; + + /** Key to SharePreference for default view (CalendarController.ViewType) */ + public static final String KEY_START_VIEW = "preferred_startView"; + /** + * Key to SharePreference for default detail view (CalendarController.ViewType) + * Typically used by widget + */ + public static final String KEY_DETAILED_VIEW = "preferred_detailedView"; + public static final String KEY_DEFAULT_CALENDAR = "preference_defaultCalendar"; + + // These must be in sync with the array preferences_week_start_day_values + public static final String WEEK_START_DEFAULT = "-1"; + public static final String WEEK_START_SATURDAY = "7"; + public static final String WEEK_START_SUNDAY = "1"; + public static final String WEEK_START_MONDAY = "2"; + + // These keys are kept to enable migrating users from previous versions + private static final String KEY_ALERTS_TYPE = "preferences_alerts_type"; + private static final String ALERT_TYPE_ALERTS = "0"; + private static final String ALERT_TYPE_STATUS_BAR = "1"; + private static final String ALERT_TYPE_OFF = "2"; + static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled"; + static final String KEY_HOME_TZ = "preferences_home_tz"; + + // Default preference values + public static final int DEFAULT_START_VIEW = CalendarController.ViewType.WEEK; + public static final int DEFAULT_DETAILED_VIEW = CalendarController.ViewType.DAY; + public static final boolean DEFAULT_SHOW_WEEK_NUM = false; + // This should match the XML file. + public static final String DEFAULT_RINGTONE = "content://settings/system/notification_sound"; + + CheckBoxPreference mAlert; + CheckBoxPreference mVibrate; + CheckBoxPreference mPopup; + CheckBoxPreference mUseHomeTZ; + CheckBoxPreference mHideDeclined; + Preference mHomeTZ; + TimeZonePickerUtils mTzPickerUtils; + ListPreference mWeekStart; + ListPreference mDefaultReminder; + + private String mTimeZoneId; + + /** Return a properly configured SharedPreferences instance */ + public static SharedPreferences getSharedPreferences(Context context) { + return context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); + } + + /** Set the default shared preferences in the proper context */ + public static void setDefaultValues(Context context) { + PreferenceManager.setDefaultValues(context, SHARED_PREFS_NAME, Context.MODE_PRIVATE, + R.xml.general_preferences, false); + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + final Activity activity = getActivity(); + + // Make sure to always use the same preferences file regardless of the package name + // we're running under + final PreferenceManager preferenceManager = getPreferenceManager(); + final SharedPreferences sharedPreferences = getSharedPreferences(activity); + preferenceManager.setSharedPreferencesName(SHARED_PREFS_NAME); + + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.general_preferences); + + final PreferenceScreen preferenceScreen = getPreferenceScreen(); + mAlert = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS); + mVibrate = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS_VIBRATE); + Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); + if (vibrator == null || !vibrator.hasVibrator()) { + PreferenceCategory mAlertGroup = (PreferenceCategory) preferenceScreen + .findPreference(KEY_ALERTS_CATEGORY); + mAlertGroup.removePreference(mVibrate); + } + + mPopup = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS_POPUP); + mUseHomeTZ = (CheckBoxPreference) preferenceScreen.findPreference(KEY_HOME_TZ_ENABLED); + mHideDeclined = (CheckBoxPreference) preferenceScreen.findPreference(KEY_HIDE_DECLINED); + mWeekStart = (ListPreference) preferenceScreen.findPreference(KEY_WEEK_START_DAY); + mDefaultReminder = (ListPreference) preferenceScreen.findPreference(KEY_DEFAULT_REMINDER); + mHomeTZ = preferenceScreen.findPreference(KEY_HOME_TZ); + mWeekStart.setSummary(mWeekStart.getEntry()); + mDefaultReminder.setSummary(mDefaultReminder.getEntry()); + + // This triggers an asynchronous call to the provider to refresh the data in shared pref + mTimeZoneId = Utils.getTimeZone(activity, null); + + SharedPreferences prefs = CalendarUtils.getSharedPreferences(activity, + Utils.SHARED_PREFS_NAME); + + // Utils.getTimeZone will return the currentTimeZone instead of the one + // in the shared_pref if home time zone is disabled. So if home tz is + // off, we will explicitly read it. + if (!prefs.getBoolean(KEY_HOME_TZ_ENABLED, false)) { + mTimeZoneId = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()); + } + + mHomeTZ.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + showTimezoneDialog(); + return true; + } + }); + + if (mTzPickerUtils == null) { + mTzPickerUtils = new TimeZonePickerUtils(getActivity()); + } + CharSequence timezoneName = mTzPickerUtils.getGmtDisplayName(getActivity(), mTimeZoneId, + System.currentTimeMillis(), false); + mHomeTZ.setSummary(timezoneName != null ? timezoneName : mTimeZoneId); + + TimeZonePickerDialog tzpd = (TimeZonePickerDialog) activity.getFragmentManager() + .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER); + if (tzpd != null) { + tzpd.setOnTimeZoneSetListener(this); + } + + migrateOldPreferences(sharedPreferences); + + updateChildPreferences(); + } + + private void showTimezoneDialog() { + final Activity activity = getActivity(); + if (activity == null) { + return; + } + + Bundle b = new Bundle(); + b.putLong(TimeZonePickerDialog.BUNDLE_START_TIME_MILLIS, System.currentTimeMillis()); + b.putString(TimeZonePickerDialog.BUNDLE_TIME_ZONE, Utils.getTimeZone(activity, null)); + + FragmentManager fm = getActivity().getFragmentManager(); + TimeZonePickerDialog tzpd = (TimeZonePickerDialog) fm + .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER); + if (tzpd != null) { + tzpd.dismiss(); + } + tzpd = new TimeZonePickerDialog(); + tzpd.setArguments(b); + tzpd.setOnTimeZoneSetListener(this); + tzpd.show(fm, FRAG_TAG_TIME_ZONE_PICKER); + } + + @Override + public void onStart() { + super.onStart(); + getPreferenceScreen().getSharedPreferences() + .registerOnSharedPreferenceChangeListener(this); + setPreferenceListeners(this); + } + + /** + * Sets up all the preference change listeners to use the specified + * listener. + */ + private void setPreferenceListeners(OnPreferenceChangeListener listener) { + mUseHomeTZ.setOnPreferenceChangeListener(listener); + mHomeTZ.setOnPreferenceChangeListener(listener); + mWeekStart.setOnPreferenceChangeListener(listener); + mDefaultReminder.setOnPreferenceChangeListener(listener); + mHideDeclined.setOnPreferenceChangeListener(listener); + mVibrate.setOnPreferenceChangeListener(listener); + } + + @Override + public void onStop() { + getPreferenceScreen().getSharedPreferences() + .unregisterOnSharedPreferenceChangeListener(this); + setPreferenceListeners(null); + super.onStop(); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + Activity a = getActivity(); + if (key.equals(KEY_ALERTS)) { + updateChildPreferences(); + if (a != null) { + Intent intent = new Intent(); + intent.setClass(a, AlertReceiver.class); + if (mAlert.isChecked()) { + intent.setAction(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS); + } else { + intent.setAction(AlertReceiver.EVENT_REMINDER_APP_ACTION); + } + a.sendBroadcast(intent); + } + } + if (a != null) { + BackupManager.dataChanged(a.getPackageName()); + } + } + + /** + * Handles time zone preference changes + */ + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + String tz; + final Activity activity = getActivity(); + if (preference == mUseHomeTZ) { + if ((Boolean)newValue) { + tz = mTimeZoneId; + } else { + tz = CalendarCache.TIMEZONE_TYPE_AUTO; + } + Utils.setTimeZone(activity, tz); + return true; + } else if (preference == mHideDeclined) { + mHideDeclined.setChecked((Boolean) newValue); + Intent intent = new Intent(Utils.getWidgetScheduledUpdateAction(activity)); + intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE); + activity.sendBroadcast(intent); + return true; + } else if (preference == mWeekStart) { + mWeekStart.setValue((String) newValue); + mWeekStart.setSummary(mWeekStart.getEntry()); + } else if (preference == mDefaultReminder) { + mDefaultReminder.setValue((String) newValue); + mDefaultReminder.setSummary(mDefaultReminder.getEntry()); + } else if (preference == mVibrate) { + mVibrate.setChecked((Boolean) newValue); + return true; + } else { + return true; + } + return false; + } + + public String getRingtoneTitleFromUri(Context context, String uri) { + if (TextUtils.isEmpty(uri)) { + return null; + } + + Ringtone ring = RingtoneManager.getRingtone(getActivity(), Uri.parse(uri)); + if (ring != null) { + return ring.getTitle(context); + } + return null; + } + + /** + * If necessary, upgrades previous versions of preferences to the current + * set of keys and values. + * @param prefs the preferences to upgrade + */ + private void migrateOldPreferences(SharedPreferences prefs) { + // If needed, migrate vibration setting from a previous version + + mVibrate.setChecked(Utils.getDefaultVibrate(getActivity(), prefs)); + + // If needed, migrate the old alerts type settin + if (!prefs.contains(KEY_ALERTS) && prefs.contains(KEY_ALERTS_TYPE)) { + String type = prefs.getString(KEY_ALERTS_TYPE, ALERT_TYPE_STATUS_BAR); + if (type.equals(ALERT_TYPE_OFF)) { + mAlert.setChecked(false); + mPopup.setChecked(false); + mPopup.setEnabled(false); + } else if (type.equals(ALERT_TYPE_STATUS_BAR)) { + mAlert.setChecked(true); + mPopup.setChecked(false); + mPopup.setEnabled(true); + } else if (type.equals(ALERT_TYPE_ALERTS)) { + mAlert.setChecked(true); + mPopup.setChecked(true); + mPopup.setEnabled(true); + } + // clear out the old setting + prefs.edit().remove(KEY_ALERTS_TYPE).commit(); + } + } + + /** + * Keeps the dependent settings in sync with the parent preference, so for + * example, when notifications are turned off, we disable the preferences + * for configuring the exact notification behavior. + */ + private void updateChildPreferences() { + if (mAlert.isChecked()) { + mVibrate.setEnabled(true); + mPopup.setEnabled(true); + } else { + mVibrate.setEnabled(false); + mPopup.setEnabled(false); + } + } + + + @Override + public boolean onPreferenceTreeClick( + PreferenceScreen preferenceScreen, Preference preference) { + final String key = preference.getKey(); + return super.onPreferenceTreeClick(preferenceScreen, preference); + } + + @Override + public void onTimeZoneSet(TimeZoneInfo tzi) { + if (mTzPickerUtils == null) { + mTzPickerUtils = new TimeZonePickerUtils(getActivity()); + } + + final CharSequence timezoneName = mTzPickerUtils.getGmtDisplayName( + getActivity(), tzi.mTzId, System.currentTimeMillis(), false); + mHomeTZ.setSummary(timezoneName); + Utils.setTimeZone(getActivity(), tzi.mTzId); + } +} -- cgit v1.2.3 From d11a1d154523e07891d9f6859a679bdf43d63d51 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 17 Jun 2021 16:00:42 +0000 Subject: AOSP/Calendar - Copy of CalendarController.java The Java code in CalendarController.java has been copied into a corresponding .kt file. Test: manual - opening both .java and .kt files shows that they are identical. Change-Id: I2104cb89ca9918432d9ff171721a9d8b750fdb04 --- src/com/android/calendar/CalendarController.kt | 713 +++++++++++++++++++++++++ 1 file changed, 713 insertions(+) create mode 100644 src/com/android/calendar/CalendarController.kt diff --git a/src/com/android/calendar/CalendarController.kt b/src/com/android/calendar/CalendarController.kt new file mode 100644 index 00000000..37286f2e --- /dev/null +++ b/src/com/android/calendar/CalendarController.kt @@ -0,0 +1,713 @@ +/* + * Copyright (C) 2010 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 static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; +import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; +import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; +import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.Activity; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Events; +import android.text.format.Time; +import android.util.Log; +import android.util.Pair; + +import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map.Entry; +import java.util.WeakHashMap; + +public class CalendarController { + private static final boolean DEBUG = false; + private static final String TAG = "CalendarController"; + + public static final String EVENT_EDIT_ON_LAUNCH = "editMode"; + + public static final int MIN_CALENDAR_YEAR = 1970; + public static final int MAX_CALENDAR_YEAR = 2036; + public static final int MIN_CALENDAR_WEEK = 0; + public static final int MAX_CALENDAR_WEEK = 3497; // weeks between 1/1/1970 and 1/1/2037 + + private final Context mContext; + + // This uses a LinkedHashMap so that we can replace fragments based on the + // view id they are being expanded into since we can't guarantee a reference + // to the handler will be findable + private final LinkedHashMap eventHandlers = + new LinkedHashMap(5); + private final LinkedList mToBeRemovedEventHandlers = new LinkedList(); + private final LinkedHashMap mToBeAddedEventHandlers = new LinkedHashMap< + Integer, EventHandler>(); + private Pair mFirstEventHandler; + private Pair mToBeAddedFirstEventHandler; + private volatile int mDispatchInProgressCounter = 0; + + private static WeakHashMap> instances = + new WeakHashMap>(); + + private final WeakHashMap filters = new WeakHashMap(1); + + private int mViewType = -1; + private int mDetailViewType = -1; + private int mPreviousViewType = -1; + private long mEventId = -1; + private final Time mTime = new Time(); + private long mDateFlags = 0; + + private final Runnable mUpdateTimezone = new Runnable() { + @Override + public void run() { + mTime.switchTimezone(Utils.getTimeZone(mContext, this)); + } + }; + + /** + * One of the event types that are sent to or from the controller + */ + public interface EventType { + // Simple view of an event + final long VIEW_EVENT = 1L << 1; + + // Full detail view in read only mode + final long VIEW_EVENT_DETAILS = 1L << 2; + + // full detail view in edit mode + final long EDIT_EVENT = 1L << 3; + + final long GO_TO = 1L << 5; + + final long EVENTS_CHANGED = 1L << 7; + + final long USER_HOME = 1L << 9; + + // date range has changed, update the title + final long UPDATE_TITLE = 1L << 10; + } + + /** + * One of the Agenda/Day/Week/Month view types + */ + public interface ViewType { + final int DETAIL = -1; + final int CURRENT = 0; + final int AGENDA = 1; + final int DAY = 2; + final int WEEK = 3; + final int MONTH = 4; + final int EDIT = 5; + final int MAX_VALUE = 5; + } + + public static class EventInfo { + + private static final long ATTENTEE_STATUS_MASK = 0xFF; + private static final long ALL_DAY_MASK = 0x100; + private static final int ATTENDEE_STATUS_NONE_MASK = 0x01; + private static final int ATTENDEE_STATUS_ACCEPTED_MASK = 0x02; + private static final int ATTENDEE_STATUS_DECLINED_MASK = 0x04; + private static final int ATTENDEE_STATUS_TENTATIVE_MASK = 0x08; + + public long eventType; // one of the EventType + public int viewType; // one of the ViewType + public long id; // event id + public Time selectedTime; // the selected time in focus + + // Event start and end times. All-day events are represented in: + // - local time for GO_TO commands + // - UTC time for VIEW_EVENT and other event-related commands + public Time startTime; + public Time endTime; + + public int x; // x coordinate in the activity space + public int y; // y coordinate in the activity space + public String query; // query for a user search + public ComponentName componentName; // used in combination with query + public String eventTitle; + public long calendarId; + + /** + * For EventType.VIEW_EVENT: + * It is the default attendee response and an all day event indicator. + * Set to Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED, + * Attendees.ATTENDEE_STATUS_DECLINED, or Attendees.ATTENDEE_STATUS_TENTATIVE. + * To signal the event is an all-day event, "or" ALL_DAY_MASK with the response. + * Alternatively, use buildViewExtraLong(), getResponse(), and isAllDay(). + *

+ * For EventType.GO_TO: + * Set to {@link #EXTRA_GOTO_TIME} to go to the specified date/time. + * Set to {@link #EXTRA_GOTO_DATE} to consider the date but ignore the time. + * Set to {@link #EXTRA_GOTO_BACK_TO_PREVIOUS} if back should bring back previous view. + * Set to {@link #EXTRA_GOTO_TODAY} if this is a user request to go to the current time. + *

+ * For EventType.UPDATE_TITLE: + * Set formatting flags for Utils.formatDateRange + */ + public long extraLong; + + public boolean isAllDay() { + if (eventType != EventType.VIEW_EVENT) { + Log.wtf(TAG, "illegal call to isAllDay , wrong event type " + eventType); + return false; + } + return ((extraLong & ALL_DAY_MASK) != 0) ? true : false; + } + + public int getResponse() { + if (eventType != EventType.VIEW_EVENT) { + Log.wtf(TAG, "illegal call to getResponse , wrong event type " + eventType); + return Attendees.ATTENDEE_STATUS_NONE; + } + + int response = (int)(extraLong & ATTENTEE_STATUS_MASK); + switch (response) { + case ATTENDEE_STATUS_NONE_MASK: + return Attendees.ATTENDEE_STATUS_NONE; + case ATTENDEE_STATUS_ACCEPTED_MASK: + return Attendees.ATTENDEE_STATUS_ACCEPTED; + case ATTENDEE_STATUS_DECLINED_MASK: + return Attendees.ATTENDEE_STATUS_DECLINED; + case ATTENDEE_STATUS_TENTATIVE_MASK: + return Attendees.ATTENDEE_STATUS_TENTATIVE; + default: + Log.wtf(TAG,"Unknown attendee response " + response); + } + return ATTENDEE_STATUS_NONE_MASK; + } + + // Used to build the extra long for a VIEW event. + public static long buildViewExtraLong(int response, boolean allDay) { + long extra = allDay ? ALL_DAY_MASK : 0; + + switch (response) { + case Attendees.ATTENDEE_STATUS_NONE: + extra |= ATTENDEE_STATUS_NONE_MASK; + break; + case Attendees.ATTENDEE_STATUS_ACCEPTED: + extra |= ATTENDEE_STATUS_ACCEPTED_MASK; + break; + case Attendees.ATTENDEE_STATUS_DECLINED: + extra |= ATTENDEE_STATUS_DECLINED_MASK; + break; + case Attendees.ATTENDEE_STATUS_TENTATIVE: + extra |= ATTENDEE_STATUS_TENTATIVE_MASK; + break; + default: + Log.wtf(TAG,"Unknown attendee response " + response); + extra |= ATTENDEE_STATUS_NONE_MASK; + break; + } + return extra; + } + } + + /** + * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time + * can be ignored + */ + public static final long EXTRA_GOTO_DATE = 1; + public static final long EXTRA_GOTO_TIME = 2; + public static final long EXTRA_GOTO_BACK_TO_PREVIOUS = 4; + public static final long EXTRA_GOTO_TODAY = 8; + + public interface EventHandler { + long getSupportedEventTypes(); + void handleEvent(EventInfo event); + + /** + * This notifies the handler that the database has changed and it should + * update its view. + */ + void eventsChanged(); + } + + /** + * Creates and/or returns an instance of CalendarController associated with + * the supplied context. It is best to pass in the current Activity. + * + * @param context The activity if at all possible. + */ + public static CalendarController getInstance(Context context) { + synchronized (instances) { + CalendarController controller = null; + WeakReference weakController = instances.get(context); + if (weakController != null) { + controller = weakController.get(); + } + + if (controller == null) { + controller = new CalendarController(context); + instances.put(context, new WeakReference(controller)); + } + return controller; + } + } + + /** + * Removes an instance when it is no longer needed. This should be called in + * an activity's onDestroy method. + * + * @param context The activity used to create the controller + */ + public static void removeInstance(Context context) { + instances.remove(context); + } + + private CalendarController(Context context) { + mContext = context; + mUpdateTimezone.run(); + mTime.setToNow(); + mDetailViewType = Utils.getSharedPreference(mContext, + GeneralPreferences.KEY_DETAILED_VIEW, + GeneralPreferences.DEFAULT_DETAILED_VIEW); + } + + public void sendEventRelatedEvent(Object sender, long eventType, long eventId, long startMillis, + long endMillis, int x, int y, long selectedMillis) { + // TODO: pass the real allDay status or at least a status that says we don't know the + // status and have the receiver query the data. + // The current use of this method for VIEW_EVENT is by the day view to show an EventInfo + // so currently the missing allDay status has no effect. + sendEventRelatedEventWithExtra(sender, eventType, eventId, startMillis, endMillis, x, y, + EventInfo.buildViewExtraLong(Attendees.ATTENDEE_STATUS_NONE, false), + selectedMillis); + } + + /** + * Helper for sending New/View/Edit/Delete events + * + * @param sender object of the caller + * @param eventType one of {@link EventType} + * @param eventId event id + * @param startMillis start time + * @param endMillis end time + * @param x x coordinate in the activity space + * @param y y coordinate in the activity space + * @param extraLong default response value for the "simple event view" and all day indication. + * Use Attendees.ATTENDEE_STATUS_NONE for no response. + * @param selectedMillis The time to specify as selected + */ + public void sendEventRelatedEventWithExtra(Object sender, long eventType, long eventId, + long startMillis, long endMillis, int x, int y, long extraLong, long selectedMillis) { + sendEventRelatedEventWithExtraWithTitleWithCalendarId(sender, eventType, eventId, + startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1); + } + + /** + * Helper for sending New/View/Edit/Delete events + * + * @param sender object of the caller + * @param eventType one of {@link EventType} + * @param eventId event id + * @param startMillis start time + * @param endMillis end time + * @param x x coordinate in the activity space + * @param y y coordinate in the activity space + * @param extraLong default response value for the "simple event view" and all day indication. + * Use Attendees.ATTENDEE_STATUS_NONE for no response. + * @param selectedMillis The time to specify as selected + * @param title The title of the event + * @param calendarId The id of the calendar which the event belongs to + */ + public void sendEventRelatedEventWithExtraWithTitleWithCalendarId(Object sender, long eventType, + long eventId, long startMillis, long endMillis, int x, int y, long extraLong, + long selectedMillis, String title, long calendarId) { + EventInfo info = new EventInfo(); + info.eventType = eventType; + if (eventType == EventType.VIEW_EVENT_DETAILS) { + info.viewType = ViewType.CURRENT; + } + + info.id = eventId; + info.startTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); + info.startTime.set(startMillis); + if (selectedMillis != -1) { + info.selectedTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); + info.selectedTime.set(selectedMillis); + } else { + info.selectedTime = info.startTime; + } + info.endTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); + info.endTime.set(endMillis); + info.x = x; + info.y = y; + info.extraLong = extraLong; + info.eventTitle = title; + info.calendarId = calendarId; + this.sendEvent(sender, info); + } + /** + * Helper for sending non-calendar-event events + * + * @param sender object of the caller + * @param eventType one of {@link EventType} + * @param start start time + * @param end end time + * @param eventId event id + * @param viewType {@link ViewType} + */ + public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId, + int viewType) { + sendEvent(sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null, + null); + } + + /** + * sendEvent() variant with extraLong, search query, and search component name. + */ + public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId, + int viewType, long extraLong, String query, ComponentName componentName) { + sendEvent(sender, eventType, start, end, start, eventId, viewType, extraLong, query, + componentName); + } + + public void sendEvent(Object sender, long eventType, Time start, Time end, Time selected, + long eventId, int viewType, long extraLong, String query, ComponentName componentName) { + EventInfo info = new EventInfo(); + info.eventType = eventType; + info.startTime = start; + info.selectedTime = selected; + info.endTime = end; + info.id = eventId; + info.viewType = viewType; + info.query = query; + info.componentName = componentName; + info.extraLong = extraLong; + this.sendEvent(sender, info); + } + + public void sendEvent(Object sender, final EventInfo event) { + // TODO Throw exception on invalid events + + if (DEBUG) { + Log.d(TAG, eventInfoToString(event)); + } + + Long filteredTypes = filters.get(sender); + if (filteredTypes != null && (filteredTypes.longValue() & event.eventType) != 0) { + // Suppress event per filter + if (DEBUG) { + Log.d(TAG, "Event suppressed"); + } + return; + } + + mPreviousViewType = mViewType; + + // Fix up view if not specified + if (event.viewType == ViewType.DETAIL) { + event.viewType = mDetailViewType; + mViewType = mDetailViewType; + } else if (event.viewType == ViewType.CURRENT) { + event.viewType = mViewType; + } else if (event.viewType != ViewType.EDIT) { + mViewType = event.viewType; + + if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY + || (Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK)) { + mDetailViewType = mViewType; + } + } + + if (DEBUG) { + Log.d(TAG, "vvvvvvvvvvvvvvv"); + Log.d(TAG, "Start " + (event.startTime == null ? "null" : event.startTime.toString())); + Log.d(TAG, "End " + (event.endTime == null ? "null" : event.endTime.toString())); + Log.d(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString())); + Log.d(TAG, "mTime " + (mTime == null ? "null" : mTime.toString())); + } + + long startMillis = 0; + if (event.startTime != null) { + startMillis = event.startTime.toMillis(false); + } + + // Set mTime if selectedTime is set + if (event.selectedTime != null && event.selectedTime.toMillis(false) != 0) { + mTime.set(event.selectedTime); + } else { + if (startMillis != 0) { + // selectedTime is not set so set mTime to startTime iff it is not + // within start and end times + long mtimeMillis = mTime.toMillis(false); + if (mtimeMillis < startMillis + || (event.endTime != null && mtimeMillis > event.endTime.toMillis(false))) { + mTime.set(event.startTime); + } + } + event.selectedTime = mTime; + } + // Store the formatting flags if this is an update to the title + if (event.eventType == EventType.UPDATE_TITLE) { + mDateFlags = event.extraLong; + } + + // Fix up start time if not specified + if (startMillis == 0) { + event.startTime = mTime; + } + if (DEBUG) { + Log.d(TAG, "Start " + (event.startTime == null ? "null" : event.startTime.toString())); + Log.d(TAG, "End " + (event.endTime == null ? "null" : event.endTime.toString())); + Log.d(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString())); + Log.d(TAG, "mTime " + (mTime == null ? "null" : mTime.toString())); + Log.d(TAG, "^^^^^^^^^^^^^^^"); + } + + // Store the eventId if we're entering edit event + if ((event.eventType + & (EventType.VIEW_EVENT_DETAILS)) + != 0) { + if (event.id > 0) { + mEventId = event.id; + } else { + mEventId = -1; + } + } + + boolean handled = false; + synchronized (this) { + mDispatchInProgressCounter ++; + + if (DEBUG) { + Log.d(TAG, "sendEvent: Dispatching to " + eventHandlers.size() + " handlers"); + } + // Dispatch to event handler(s) + if (mFirstEventHandler != null) { + // Handle the 'first' one before handling the others + EventHandler handler = mFirstEventHandler.second; + if (handler != null && (handler.getSupportedEventTypes() & event.eventType) != 0 + && !mToBeRemovedEventHandlers.contains(mFirstEventHandler.first)) { + handler.handleEvent(event); + handled = true; + } + } + for (Iterator> handlers = + eventHandlers.entrySet().iterator(); handlers.hasNext();) { + Entry entry = handlers.next(); + int key = entry.getKey(); + if (mFirstEventHandler != null && key == mFirstEventHandler.first) { + // If this was the 'first' handler it was already handled + continue; + } + EventHandler eventHandler = entry.getValue(); + if (eventHandler != null + && (eventHandler.getSupportedEventTypes() & event.eventType) != 0) { + if (mToBeRemovedEventHandlers.contains(key)) { + continue; + } + eventHandler.handleEvent(event); + handled = true; + } + } + + mDispatchInProgressCounter --; + + if (mDispatchInProgressCounter == 0) { + + // Deregister removed handlers + if (mToBeRemovedEventHandlers.size() > 0) { + for (Integer zombie : mToBeRemovedEventHandlers) { + eventHandlers.remove(zombie); + if (mFirstEventHandler != null && zombie.equals(mFirstEventHandler.first)) { + mFirstEventHandler = null; + } + } + mToBeRemovedEventHandlers.clear(); + } + // Add new handlers + if (mToBeAddedFirstEventHandler != null) { + mFirstEventHandler = mToBeAddedFirstEventHandler; + mToBeAddedFirstEventHandler = null; + } + if (mToBeAddedEventHandlers.size() > 0) { + for (Entry food : mToBeAddedEventHandlers.entrySet()) { + eventHandlers.put(food.getKey(), food.getValue()); + } + } + } + } + } + + /** + * Adds or updates an event handler. This uses a LinkedHashMap so that we can + * replace fragments based on the view id they are being expanded into. + * + * @param key The view id or placeholder for this handler + * @param eventHandler Typically a fragment or activity in the calendar app + */ + public void registerEventHandler(int key, EventHandler eventHandler) { + synchronized (this) { + if (mDispatchInProgressCounter > 0) { + mToBeAddedEventHandlers.put(key, eventHandler); + } else { + eventHandlers.put(key, eventHandler); + } + } + } + + public void registerFirstEventHandler(int key, EventHandler eventHandler) { + synchronized (this) { + registerEventHandler(key, eventHandler); + if (mDispatchInProgressCounter > 0) { + mToBeAddedFirstEventHandler = new Pair(key, eventHandler); + } else { + mFirstEventHandler = new Pair(key, eventHandler); + } + } + } + + public void deregisterEventHandler(Integer key) { + synchronized (this) { + if (mDispatchInProgressCounter > 0) { + // To avoid ConcurrencyException, stash away the event handler for now. + mToBeRemovedEventHandlers.add(key); + } else { + eventHandlers.remove(key); + if (mFirstEventHandler != null && mFirstEventHandler.first == key) { + mFirstEventHandler = null; + } + } + } + } + + public void deregisterAllEventHandlers() { + synchronized (this) { + if (mDispatchInProgressCounter > 0) { + // To avoid ConcurrencyException, stash away the event handler for now. + mToBeRemovedEventHandlers.addAll(eventHandlers.keySet()); + } else { + eventHandlers.clear(); + mFirstEventHandler = null; + } + } + } + + // FRAG_TODO doesn't work yet + public void filterBroadcasts(Object sender, long eventTypes) { + filters.put(sender, eventTypes); + } + + /** + * @return the time that this controller is currently pointed at + */ + public long getTime() { + return mTime.toMillis(false); + } + + /** + * @return the last set of date flags sent with + * {@link EventType#UPDATE_TITLE} + */ + public long getDateFlags() { + return mDateFlags; + } + + /** + * Set the time this controller is currently pointed at + * + * @param millisTime Time since epoch in millis + */ + public void setTime(long millisTime) { + mTime.set(millisTime); + } + + /** + * @return the last event ID the edit view was launched with + */ + public long getEventId() { + return mEventId; + } + + public int getViewType() { + return mViewType; + } + + public int getPreviousViewType() { + return mPreviousViewType; + } + + public void launchViewEvent(long eventId, long startMillis, long endMillis, int response) { + Intent intent = new Intent(Intent.ACTION_VIEW); + Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId); + intent.setData(eventUri); + intent.setClass(mContext, AllInOneActivity.class); + intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis); + intent.putExtra(EXTRA_EVENT_END_TIME, endMillis); + intent.putExtra(ATTENDEE_STATUS, response); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + mContext.startActivity(intent); + } + + // Forces the viewType. Should only be used for initialization. + public void setViewType(int viewType) { + mViewType = viewType; + } + + // Sets the eventId. Should only be used for initialization. + public void setEventId(long eventId) { + mEventId = eventId; + } + + private String eventInfoToString(EventInfo eventInfo) { + String tmp = "Unknown"; + + StringBuilder builder = new StringBuilder(); + if ((eventInfo.eventType & EventType.GO_TO) != 0) { + tmp = "Go to time/event"; + } else if ((eventInfo.eventType & EventType.VIEW_EVENT) != 0) { + tmp = "View event"; + } else if ((eventInfo.eventType & EventType.VIEW_EVENT_DETAILS) != 0) { + tmp = "View details"; + } else if ((eventInfo.eventType & EventType.EVENTS_CHANGED) != 0) { + tmp = "Refresh events"; + } else if ((eventInfo.eventType & EventType.USER_HOME) != 0) { + tmp = "Gone home"; + } else if ((eventInfo.eventType & EventType.UPDATE_TITLE) != 0) { + tmp = "Update title"; + } + builder.append(tmp); + builder.append(": id="); + builder.append(eventInfo.id); + builder.append(", selected="); + builder.append(eventInfo.selectedTime); + builder.append(", start="); + builder.append(eventInfo.startTime); + builder.append(", end="); + builder.append(eventInfo.endTime); + builder.append(", viewType="); + builder.append(eventInfo.viewType); + builder.append(", x="); + builder.append(eventInfo.x); + builder.append(", y="); + builder.append(eventInfo.y); + return builder.toString(); + } +} -- cgit v1.2.3 From a67aaceaa1104525d40ff11f8b03d950d73b4fd7 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 18 Jun 2021 20:22:14 +0000 Subject: AOSP/Calendar CalendarController fully converted with bp file Using Android Studio, CalendarController.java was fully converted to Kotlin. The converter failed to apply override modifiers for functions that required it. Moreover, the @JvmStatic annotation was not added properly to certain static functions so this had to be manually added. Another recurring issue had to do with the converter not correctly casting certain objects to their correct dynamic type. One unique problem that arose with this conversion was access to the instance variables of the static inner class EventInfo. Without the @JvmField for each said instance field, Another class in the "calendar" package called AllInOneActivity could not successfully access these properties. Consequently, I had to add the aforementioned annotation manually to avoid compile errors. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.143 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 6m 1s Total aggregated tests run time: 6m 1s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 1.04 tests/sec [376 tests / 361370 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 6386 ms || clean = 2025 ms Total preparation time: 6s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 7m 36s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: I7c712ba3d3b915a1baaa2833c9693eece7f352e3 --- Android.bp | 1 + src/com/android/calendar/CalendarController.kt | 984 +++++++++++++------------ 2 files changed, 508 insertions(+), 477 deletions(-) diff --git a/Android.bp b/Android.bp index 99d07cf3..4e229214 100644 --- a/Android.bp +++ b/Android.bp @@ -27,6 +27,7 @@ exclude_srcsd = [ "src/**/calendar/alerts/InitAlarmsService.java", "src/**/calendar/alerts/NotificationMgr.java", "src/**/calendar/alerts/QuickResponseActivity.java", + "src/**/calendar/CalendarController.java", "src/**/calendar/DayOfMonthDrawable.java", "src/**/calendar/EventInfoActivity.java", ] diff --git a/src/com/android/calendar/CalendarController.kt b/src/com/android/calendar/CalendarController.kt index 37286f2e..b5484ffc 100644 --- a/src/com/android/calendar/CalendarController.kt +++ b/src/com/android/calendar/CalendarController.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,145 +13,119 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar; - -import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; -import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; -import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; -import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.Activity; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.text.format.Time; -import android.util.Log; -import android.util.Pair; - -import java.lang.ref.WeakReference; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.Map.Entry; -import java.util.WeakHashMap; - -public class CalendarController { - private static final boolean DEBUG = false; - private static final String TAG = "CalendarController"; - - public static final String EVENT_EDIT_ON_LAUNCH = "editMode"; - - public static final int MIN_CALENDAR_YEAR = 1970; - public static final int MAX_CALENDAR_YEAR = 2036; - public static final int MIN_CALENDAR_WEEK = 0; - public static final int MAX_CALENDAR_WEEK = 3497; // weeks between 1/1/1970 and 1/1/2037 - - private final Context mContext; +package com.android.calendar + +import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME +import android.provider.CalendarContract.EXTRA_EVENT_END_TIME +import android.provider.CalendarContract.Attendees.ATTENDEE_STATUS +import android.content.ComponentName +import android.content.ContentUris +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.provider.CalendarContract.Attendees +import android.provider.CalendarContract.Events +import android.text.format.Time +import android.util.Log +import android.util.Pair +import java.lang.ref.WeakReference +import java.util.LinkedHashMap +import java.util.LinkedList +import java.util.WeakHashMap + +class CalendarController private constructor(context: Context) { + private var mContext: Context? = null // This uses a LinkedHashMap so that we can replace fragments based on the // view id they are being expanded into since we can't guarantee a reference // to the handler will be findable - private final LinkedHashMap eventHandlers = - new LinkedHashMap(5); - private final LinkedList mToBeRemovedEventHandlers = new LinkedList(); - private final LinkedHashMap mToBeAddedEventHandlers = new LinkedHashMap< - Integer, EventHandler>(); - private Pair mFirstEventHandler; - private Pair mToBeAddedFirstEventHandler; - private volatile int mDispatchInProgressCounter = 0; - - private static WeakHashMap> instances = - new WeakHashMap>(); - - private final WeakHashMap filters = new WeakHashMap(1); - - private int mViewType = -1; - private int mDetailViewType = -1; - private int mPreviousViewType = -1; - private long mEventId = -1; - private final Time mTime = new Time(); - private long mDateFlags = 0; - - private final Runnable mUpdateTimezone = new Runnable() { + private val eventHandlers: LinkedHashMap = + LinkedHashMap(5) + private val mToBeRemovedEventHandlers: LinkedList = LinkedList() + private val mToBeAddedEventHandlers: LinkedHashMap = + LinkedHashMap() + private var mFirstEventHandler: Pair? = null + private var mToBeAddedFirstEventHandler: Pair? = null + + @Volatile + private var mDispatchInProgressCounter = 0 + private val filters: WeakHashMap = WeakHashMap(1) + + // Forces the viewType. Should only be used for initialization. + var viewType = -1 + private var mDetailViewType = -1 + var previousViewType = -1 + private set + + // The last event ID the edit view was launched with + var eventId: Long = -1 + private val mTime: Time? = Time() + + // The last set of date flags sent with + var dateFlags: Long = 0 + private set + private val mUpdateTimezone: Runnable = object : Runnable { @Override - public void run() { - mTime.switchTimezone(Utils.getTimeZone(mContext, this)); + override fun run() { + mTime?.switchTimezone(Utils.getTimeZone(mContext, this)) } - }; + } /** * One of the event types that are sent to or from the controller */ - public interface EventType { - // Simple view of an event - final long VIEW_EVENT = 1L << 1; - - // Full detail view in read only mode - final long VIEW_EVENT_DETAILS = 1L << 2; - - // full detail view in edit mode - final long EDIT_EVENT = 1L << 3; - - final long GO_TO = 1L << 5; - - final long EVENTS_CHANGED = 1L << 7; - - final long USER_HOME = 1L << 9; - - // date range has changed, update the title - final long UPDATE_TITLE = 1L << 10; + interface EventType { + companion object { + // Simple view of an event + const val VIEW_EVENT = 1L shl 1 + + // Full detail view in read only mode + const val VIEW_EVENT_DETAILS = 1L shl 2 + + // full detail view in edit mode + const val EDIT_EVENT = 1L shl 3 + const val GO_TO = 1L shl 5 + const val EVENTS_CHANGED = 1L shl 7 + const val USER_HOME = 1L shl 9 + + // date range has changed, update the title + const val UPDATE_TITLE = 1L shl 10 + } } /** * One of the Agenda/Day/Week/Month view types */ - public interface ViewType { - final int DETAIL = -1; - final int CURRENT = 0; - final int AGENDA = 1; - final int DAY = 2; - final int WEEK = 3; - final int MONTH = 4; - final int EDIT = 5; - final int MAX_VALUE = 5; + interface ViewType { + companion object { + const val DETAIL = -1 + const val CURRENT = 0 + const val AGENDA = 1 + const val DAY = 2 + const val WEEK = 3 + const val MONTH = 4 + const val EDIT = 5 + const val MAX_VALUE = 5 + } } - public static class EventInfo { - - private static final long ATTENTEE_STATUS_MASK = 0xFF; - private static final long ALL_DAY_MASK = 0x100; - private static final int ATTENDEE_STATUS_NONE_MASK = 0x01; - private static final int ATTENDEE_STATUS_ACCEPTED_MASK = 0x02; - private static final int ATTENDEE_STATUS_DECLINED_MASK = 0x04; - private static final int ATTENDEE_STATUS_TENTATIVE_MASK = 0x08; - - public long eventType; // one of the EventType - public int viewType; // one of the ViewType - public long id; // event id - public Time selectedTime; // the selected time in focus + class EventInfo { + @JvmField var eventType: Long = 0 // one of the EventType + @JvmField var viewType = 0 // one of the ViewType + @JvmField var id: Long = 0 // event id + @JvmField var selectedTime: Time? = null // the selected time in focus // Event start and end times. All-day events are represented in: // - local time for GO_TO commands // - UTC time for VIEW_EVENT and other event-related commands - public Time startTime; - public Time endTime; - - public int x; // x coordinate in the activity space - public int y; // y coordinate in the activity space - public String query; // query for a user search - public ComponentName componentName; // used in combination with query - public String eventTitle; - public long calendarId; + @JvmField var startTime: Time? = null + @JvmField var endTime: Time? = null + @JvmField var x = 0 // x coordinate in the activity space + @JvmField var y = 0 // y coordinate in the activity space + @JvmField var query: String? = null // query for a user search + @JvmField var componentName: ComponentName? = null // used in combination with query + @JvmField var eventTitle: String? = null + @JvmField var calendarId: Long = 0 /** * For EventType.VIEW_EVENT: @@ -160,396 +134,424 @@ public class CalendarController { * Attendees.ATTENDEE_STATUS_DECLINED, or Attendees.ATTENDEE_STATUS_TENTATIVE. * To signal the event is an all-day event, "or" ALL_DAY_MASK with the response. * Alternatively, use buildViewExtraLong(), getResponse(), and isAllDay(). - *

+ * + * * For EventType.GO_TO: - * Set to {@link #EXTRA_GOTO_TIME} to go to the specified date/time. - * Set to {@link #EXTRA_GOTO_DATE} to consider the date but ignore the time. - * Set to {@link #EXTRA_GOTO_BACK_TO_PREVIOUS} if back should bring back previous view. - * Set to {@link #EXTRA_GOTO_TODAY} if this is a user request to go to the current time. - *

+ * Set to [.EXTRA_GOTO_TIME] to go to the specified date/time. + * Set to [.EXTRA_GOTO_DATE] to consider the date but ignore the time. + * Set to [.EXTRA_GOTO_BACK_TO_PREVIOUS] if back should bring back previous view. + * Set to [.EXTRA_GOTO_TODAY] if this is a user request to go to the current time. + * + * * For EventType.UPDATE_TITLE: * Set formatting flags for Utils.formatDateRange */ - public long extraLong; - - public boolean isAllDay() { - if (eventType != EventType.VIEW_EVENT) { - Log.wtf(TAG, "illegal call to isAllDay , wrong event type " + eventType); - return false; - } - return ((extraLong & ALL_DAY_MASK) != 0) ? true : false; - } - - public int getResponse() { - if (eventType != EventType.VIEW_EVENT) { - Log.wtf(TAG, "illegal call to getResponse , wrong event type " + eventType); - return Attendees.ATTENDEE_STATUS_NONE; + @JvmField var extraLong: Long = 0 + val isAllDay: Boolean + get() { + if (eventType != EventType.VIEW_EVENT) { + Log.wtf(TAG, "illegal call to isAllDay , wrong event type $eventType") + return false + } + return if (extraLong and ALL_DAY_MASK != 0L) true else false } - - int response = (int)(extraLong & ATTENTEE_STATUS_MASK); - switch (response) { - case ATTENDEE_STATUS_NONE_MASK: - return Attendees.ATTENDEE_STATUS_NONE; - case ATTENDEE_STATUS_ACCEPTED_MASK: - return Attendees.ATTENDEE_STATUS_ACCEPTED; - case ATTENDEE_STATUS_DECLINED_MASK: - return Attendees.ATTENDEE_STATUS_DECLINED; - case ATTENDEE_STATUS_TENTATIVE_MASK: - return Attendees.ATTENDEE_STATUS_TENTATIVE; - default: - Log.wtf(TAG,"Unknown attendee response " + response); + val response: Int + get() { + if (eventType != EventType.VIEW_EVENT) { + Log.wtf(TAG, "illegal call to getResponse , wrong event type $eventType") + return Attendees.ATTENDEE_STATUS_NONE + } + val response = (extraLong and ATTENTEE_STATUS_MASK).toInt() + when (response) { + ATTENDEE_STATUS_NONE_MASK -> return Attendees.ATTENDEE_STATUS_NONE + ATTENDEE_STATUS_ACCEPTED_MASK -> return Attendees.ATTENDEE_STATUS_ACCEPTED + ATTENDEE_STATUS_DECLINED_MASK -> return Attendees.ATTENDEE_STATUS_DECLINED + ATTENDEE_STATUS_TENTATIVE_MASK -> return Attendees.ATTENDEE_STATUS_TENTATIVE + else -> Log.wtf(TAG, "Unknown attendee response $response") + } + return ATTENDEE_STATUS_NONE_MASK } - return ATTENDEE_STATUS_NONE_MASK; - } - // Used to build the extra long for a VIEW event. - public static long buildViewExtraLong(int response, boolean allDay) { - long extra = allDay ? ALL_DAY_MASK : 0; - - switch (response) { - case Attendees.ATTENDEE_STATUS_NONE: - extra |= ATTENDEE_STATUS_NONE_MASK; - break; - case Attendees.ATTENDEE_STATUS_ACCEPTED: - extra |= ATTENDEE_STATUS_ACCEPTED_MASK; - break; - case Attendees.ATTENDEE_STATUS_DECLINED: - extra |= ATTENDEE_STATUS_DECLINED_MASK; - break; - case Attendees.ATTENDEE_STATUS_TENTATIVE: - extra |= ATTENDEE_STATUS_TENTATIVE_MASK; - break; - default: - Log.wtf(TAG,"Unknown attendee response " + response); - extra |= ATTENDEE_STATUS_NONE_MASK; - break; + companion object { + private const val ATTENTEE_STATUS_MASK: Long = 0xFF + private const val ALL_DAY_MASK: Long = 0x100 + private const val ATTENDEE_STATUS_NONE_MASK = 0x01 + private const val ATTENDEE_STATUS_ACCEPTED_MASK = 0x02 + private const val ATTENDEE_STATUS_DECLINED_MASK = 0x04 + private const val ATTENDEE_STATUS_TENTATIVE_MASK = 0x08 + + // Used to build the extra long for a VIEW event. + @JvmStatic fun buildViewExtraLong(response: Int, allDay: Boolean): Long { + var extra = if (allDay) ALL_DAY_MASK else 0 + extra = when (response) { + Attendees.ATTENDEE_STATUS_NONE -> extra or + ATTENDEE_STATUS_NONE_MASK.toLong() + Attendees.ATTENDEE_STATUS_ACCEPTED -> extra or + ATTENDEE_STATUS_ACCEPTED_MASK.toLong() + Attendees.ATTENDEE_STATUS_DECLINED -> extra or + ATTENDEE_STATUS_DECLINED_MASK.toLong() + Attendees.ATTENDEE_STATUS_TENTATIVE -> extra or + ATTENDEE_STATUS_TENTATIVE_MASK.toLong() + else -> { + Log.wtf( + TAG, + "Unknown attendee response $response" + ) + extra or ATTENDEE_STATUS_NONE_MASK.toLong() + } + } + return extra } - return extra; } } - /** - * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time - * can be ignored - */ - public static final long EXTRA_GOTO_DATE = 1; - public static final long EXTRA_GOTO_TIME = 2; - public static final long EXTRA_GOTO_BACK_TO_PREVIOUS = 4; - public static final long EXTRA_GOTO_TODAY = 8; - - public interface EventHandler { - long getSupportedEventTypes(); - void handleEvent(EventInfo event); + interface EventHandler { + val supportedEventTypes: Long + fun handleEvent(event: EventInfo?) /** * This notifies the handler that the database has changed and it should * update its view. */ - void eventsChanged(); - } - - /** - * Creates and/or returns an instance of CalendarController associated with - * the supplied context. It is best to pass in the current Activity. - * - * @param context The activity if at all possible. - */ - public static CalendarController getInstance(Context context) { - synchronized (instances) { - CalendarController controller = null; - WeakReference weakController = instances.get(context); - if (weakController != null) { - controller = weakController.get(); - } - - if (controller == null) { - controller = new CalendarController(context); - instances.put(context, new WeakReference(controller)); - } - return controller; - } - } - - /** - * Removes an instance when it is no longer needed. This should be called in - * an activity's onDestroy method. - * - * @param context The activity used to create the controller - */ - public static void removeInstance(Context context) { - instances.remove(context); + fun eventsChanged() } - private CalendarController(Context context) { - mContext = context; - mUpdateTimezone.run(); - mTime.setToNow(); - mDetailViewType = Utils.getSharedPreference(mContext, - GeneralPreferences.KEY_DETAILED_VIEW, - GeneralPreferences.DEFAULT_DETAILED_VIEW); - } - - public void sendEventRelatedEvent(Object sender, long eventType, long eventId, long startMillis, - long endMillis, int x, int y, long selectedMillis) { + fun sendEventRelatedEvent( + sender: Object?, + eventType: Long, + eventId: Long, + startMillis: Long, + endMillis: Long, + x: Int, + y: Int, + selectedMillis: Long + ) { // TODO: pass the real allDay status or at least a status that says we don't know the // status and have the receiver query the data. // The current use of this method for VIEW_EVENT is by the day view to show an EventInfo // so currently the missing allDay status has no effect. - sendEventRelatedEventWithExtra(sender, eventType, eventId, startMillis, endMillis, x, y, - EventInfo.buildViewExtraLong(Attendees.ATTENDEE_STATUS_NONE, false), - selectedMillis); + sendEventRelatedEventWithExtra( + sender, eventType, eventId, startMillis, endMillis, x, y, + EventInfo.buildViewExtraLong(Attendees.ATTENDEE_STATUS_NONE, false), + selectedMillis + ) } /** * Helper for sending New/View/Edit/Delete events * * @param sender object of the caller - * @param eventType one of {@link EventType} + * @param eventType one of [EventType] * @param eventId event id * @param startMillis start time * @param endMillis end time * @param x x coordinate in the activity space * @param y y coordinate in the activity space * @param extraLong default response value for the "simple event view" and all day indication. - * Use Attendees.ATTENDEE_STATUS_NONE for no response. + * Use Attendees.ATTENDEE_STATUS_NONE for no response. * @param selectedMillis The time to specify as selected */ - public void sendEventRelatedEventWithExtra(Object sender, long eventType, long eventId, - long startMillis, long endMillis, int x, int y, long extraLong, long selectedMillis) { - sendEventRelatedEventWithExtraWithTitleWithCalendarId(sender, eventType, eventId, - startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1); + fun sendEventRelatedEventWithExtra( + sender: Object?, + eventType: Long, + eventId: Long, + startMillis: Long, + endMillis: Long, + x: Int, + y: Int, + extraLong: Long, + selectedMillis: Long + ) { + sendEventRelatedEventWithExtraWithTitleWithCalendarId( + sender, eventType, eventId, + startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1 + ) } /** * Helper for sending New/View/Edit/Delete events * * @param sender object of the caller - * @param eventType one of {@link EventType} + * @param eventType one of [EventType] * @param eventId event id * @param startMillis start time * @param endMillis end time * @param x x coordinate in the activity space * @param y y coordinate in the activity space * @param extraLong default response value for the "simple event view" and all day indication. - * Use Attendees.ATTENDEE_STATUS_NONE for no response. + * Use Attendees.ATTENDEE_STATUS_NONE for no response. * @param selectedMillis The time to specify as selected * @param title The title of the event * @param calendarId The id of the calendar which the event belongs to */ - public void sendEventRelatedEventWithExtraWithTitleWithCalendarId(Object sender, long eventType, - long eventId, long startMillis, long endMillis, int x, int y, long extraLong, - long selectedMillis, String title, long calendarId) { - EventInfo info = new EventInfo(); - info.eventType = eventType; + fun sendEventRelatedEventWithExtraWithTitleWithCalendarId( + sender: Object?, + eventType: Long, + eventId: Long, + startMillis: Long, + endMillis: Long, + x: Int, + y: Int, + extraLong: Long, + selectedMillis: Long, + title: String?, + calendarId: Long + ) { + val info = EventInfo() + info.eventType = eventType if (eventType == EventType.VIEW_EVENT_DETAILS) { - info.viewType = ViewType.CURRENT; + info.viewType = ViewType.CURRENT } - - info.id = eventId; - info.startTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); - info.startTime.set(startMillis); - if (selectedMillis != -1) { - info.selectedTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); - info.selectedTime.set(selectedMillis); + info.id = eventId + info.startTime = Time(Utils.getTimeZone(mContext, mUpdateTimezone)) + (info.startTime as Time).set(startMillis) + if (selectedMillis != -1L) { + info.selectedTime = Time(Utils.getTimeZone(mContext, mUpdateTimezone)) + (info.selectedTime as Time).set(selectedMillis) } else { - info.selectedTime = info.startTime; + info.selectedTime = info.startTime } - info.endTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); - info.endTime.set(endMillis); - info.x = x; - info.y = y; - info.extraLong = extraLong; - info.eventTitle = title; - info.calendarId = calendarId; - this.sendEvent(sender, info); + info.endTime = Time(Utils.getTimeZone(mContext, mUpdateTimezone)) + (info.endTime as Time).set(endMillis) + info.x = x + info.y = y + info.extraLong = extraLong + info.eventTitle = title + info.calendarId = calendarId + this.sendEvent(sender, info) } + /** * Helper for sending non-calendar-event events * * @param sender object of the caller - * @param eventType one of {@link EventType} + * @param eventType one of [EventType] * @param start start time * @param end end time * @param eventId event id - * @param viewType {@link ViewType} + * @param viewType [ViewType] */ - public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId, - int viewType) { - sendEvent(sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null, - null); + fun sendEvent( + sender: Object?, + eventType: Long, + start: Time?, + end: Time?, + eventId: Long, + viewType: Int + ) { + sendEvent( + sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null, + null + ) } /** * sendEvent() variant with extraLong, search query, and search component name. */ - public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId, - int viewType, long extraLong, String query, ComponentName componentName) { - sendEvent(sender, eventType, start, end, start, eventId, viewType, extraLong, query, - componentName); + fun sendEvent( + sender: Object?, + eventType: Long, + start: Time?, + end: Time?, + eventId: Long, + viewType: Int, + extraLong: Long, + query: String?, + componentName: ComponentName? + ) { + sendEvent( + sender, eventType, start, end, start, eventId, viewType, extraLong, query, + componentName + ) } - public void sendEvent(Object sender, long eventType, Time start, Time end, Time selected, - long eventId, int viewType, long extraLong, String query, ComponentName componentName) { - EventInfo info = new EventInfo(); - info.eventType = eventType; - info.startTime = start; - info.selectedTime = selected; - info.endTime = end; - info.id = eventId; - info.viewType = viewType; - info.query = query; - info.componentName = componentName; - info.extraLong = extraLong; - this.sendEvent(sender, info); + fun sendEvent( + sender: Object?, + eventType: Long, + start: Time?, + end: Time?, + selected: Time?, + eventId: Long, + viewType: Int, + extraLong: Long, + query: String?, + componentName: ComponentName? + ) { + val info = EventInfo() + info.eventType = eventType + info.startTime = start + info.selectedTime = selected + info.endTime = end + info.id = eventId + info.viewType = viewType + info.query = query + info.componentName = componentName + info.extraLong = extraLong + this.sendEvent(sender, info) } - public void sendEvent(Object sender, final EventInfo event) { + fun sendEvent(sender: Object?, event: EventInfo) { // TODO Throw exception on invalid events - if (DEBUG) { - Log.d(TAG, eventInfoToString(event)); + Log.d(TAG, eventInfoToString(event)) } - - Long filteredTypes = filters.get(sender); - if (filteredTypes != null && (filteredTypes.longValue() & event.eventType) != 0) { + val filteredTypes: Long? = filters.get(sender) + if (filteredTypes != null && filteredTypes.toLong() and event.eventType != 0L) { // Suppress event per filter if (DEBUG) { - Log.d(TAG, "Event suppressed"); + Log.d(TAG, "Event suppressed") } - return; + return } - - mPreviousViewType = mViewType; + previousViewType = viewType // Fix up view if not specified if (event.viewType == ViewType.DETAIL) { - event.viewType = mDetailViewType; - mViewType = mDetailViewType; + event.viewType = mDetailViewType + viewType = mDetailViewType } else if (event.viewType == ViewType.CURRENT) { - event.viewType = mViewType; + event.viewType = viewType } else if (event.viewType != ViewType.EDIT) { - mViewType = event.viewType; - - if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY - || (Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK)) { - mDetailViewType = mViewType; + viewType = event.viewType + if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY || + Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK) { + mDetailViewType = viewType } } - if (DEBUG) { - Log.d(TAG, "vvvvvvvvvvvvvvv"); - Log.d(TAG, "Start " + (event.startTime == null ? "null" : event.startTime.toString())); - Log.d(TAG, "End " + (event.endTime == null ? "null" : event.endTime.toString())); - Log.d(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString())); - Log.d(TAG, "mTime " + (mTime == null ? "null" : mTime.toString())); + Log.d(TAG, "vvvvvvvvvvvvvvv") + Log.d( + TAG, + "Start " + if (event.startTime == null) "null" else event.startTime.toString() + ) + Log.d(TAG, "End " + if (event.endTime == null) "null" else event.endTime.toString()) + Log.d( + TAG, + "Select " + if (event.selectedTime == null) "null" + else event.selectedTime.toString() + ) + Log.d(TAG, "mTime " + if (mTime == null) "null" else mTime.toString()) } - - long startMillis = 0; - if (event.startTime != null) { - startMillis = event.startTime.toMillis(false); + var startMillis: Long = 0 + val temp = event.startTime + if (temp != null) { + startMillis = (event.startTime as Time).toMillis(false) } // Set mTime if selectedTime is set - if (event.selectedTime != null && event.selectedTime.toMillis(false) != 0) { - mTime.set(event.selectedTime); + val temp1 = event.selectedTime + if (temp1 != null && temp1?.toMillis(false) != 0L) { + mTime?.set(event.selectedTime) } else { - if (startMillis != 0) { + if (startMillis != 0L) { // selectedTime is not set so set mTime to startTime iff it is not // within start and end times - long mtimeMillis = mTime.toMillis(false); - if (mtimeMillis < startMillis - || (event.endTime != null && mtimeMillis > event.endTime.toMillis(false))) { - mTime.set(event.startTime); + val mtimeMillis: Long = mTime?.toMillis(false) as Long + val temp2 = event.endTime + if (mtimeMillis < startMillis || + temp2 != null && mtimeMillis > temp2.toMillis(false)) { + mTime?.set(event.startTime) } } - event.selectedTime = mTime; + event.selectedTime = mTime } // Store the formatting flags if this is an update to the title if (event.eventType == EventType.UPDATE_TITLE) { - mDateFlags = event.extraLong; + dateFlags = event.extraLong } // Fix up start time if not specified - if (startMillis == 0) { - event.startTime = mTime; + if (startMillis == 0L) { + event.startTime = mTime } if (DEBUG) { - Log.d(TAG, "Start " + (event.startTime == null ? "null" : event.startTime.toString())); - Log.d(TAG, "End " + (event.endTime == null ? "null" : event.endTime.toString())); - Log.d(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString())); - Log.d(TAG, "mTime " + (mTime == null ? "null" : mTime.toString())); - Log.d(TAG, "^^^^^^^^^^^^^^^"); + Log.d( + TAG, + "Start " + if (event.startTime == null) "null" else + event.startTime.toString() + ) + Log.d(TAG, "End " + if (event.endTime == null) "null" else + event.endTime.toString()) + Log.d( + TAG, + "Select " + if (event.selectedTime == null) "null" else + event.selectedTime.toString() + ) + Log.d(TAG, "mTime " + if (mTime == null) "null" else mTime.toString()) + Log.d(TAG, "^^^^^^^^^^^^^^^") } // Store the eventId if we're entering edit event - if ((event.eventType - & (EventType.VIEW_EVENT_DETAILS)) - != 0) { + if ((event.eventType and EventType.VIEW_EVENT_DETAILS) != 0L) { if (event.id > 0) { - mEventId = event.id; + eventId = event.id } else { - mEventId = -1; + eventId = -1 } } - - boolean handled = false; - synchronized (this) { - mDispatchInProgressCounter ++; - + var handled = false + synchronized(this) { + mDispatchInProgressCounter++ if (DEBUG) { - Log.d(TAG, "sendEvent: Dispatching to " + eventHandlers.size() + " handlers"); + Log.d( + TAG, + "sendEvent: Dispatching to " + eventHandlers.size.toString() + " handlers" + ) } // Dispatch to event handler(s) - if (mFirstEventHandler != null) { + val temp3 = mFirstEventHandler + if (temp3 != null) { // Handle the 'first' one before handling the others - EventHandler handler = mFirstEventHandler.second; - if (handler != null && (handler.getSupportedEventTypes() & event.eventType) != 0 - && !mToBeRemovedEventHandlers.contains(mFirstEventHandler.first)) { - handler.handleEvent(event); - handled = true; + val handler: EventHandler? = mFirstEventHandler?.second + if (handler != null && handler.supportedEventTypes and event.eventType != 0L && + !mToBeRemovedEventHandlers.contains(mFirstEventHandler?.first)) { + handler.handleEvent(event) + handled = true } } - for (Iterator> handlers = - eventHandlers.entrySet().iterator(); handlers.hasNext();) { - Entry entry = handlers.next(); - int key = entry.getKey(); - if (mFirstEventHandler != null && key == mFirstEventHandler.first) { + val handlers: MutableIterator> = eventHandlers.entries.iterator() + while (handlers.hasNext()) { + val entry: MutableMap.MutableEntry = handlers.next() + val key: Int = entry.key.toInt() + val temp4 = mFirstEventHandler + if (temp4 != null && key.toInt() == temp4.first.toInt()) { // If this was the 'first' handler it was already handled - continue; + continue } - EventHandler eventHandler = entry.getValue(); - if (eventHandler != null - && (eventHandler.getSupportedEventTypes() & event.eventType) != 0) { - if (mToBeRemovedEventHandlers.contains(key)) { - continue; + val eventHandler: EventHandler = entry.value + if (eventHandler != null && + eventHandler.supportedEventTypes and event.eventType != 0L) { + if (mToBeRemovedEventHandlers.contains(key as Integer)) { + continue } - eventHandler.handleEvent(event); - handled = true; + eventHandler.handleEvent(event) + handled = true } } - - mDispatchInProgressCounter --; - + mDispatchInProgressCounter-- if (mDispatchInProgressCounter == 0) { // Deregister removed handlers - if (mToBeRemovedEventHandlers.size() > 0) { - for (Integer zombie : mToBeRemovedEventHandlers) { - eventHandlers.remove(zombie); - if (mFirstEventHandler != null && zombie.equals(mFirstEventHandler.first)) { - mFirstEventHandler = null; + if (mToBeRemovedEventHandlers.size > 0) { + for (zombie in mToBeRemovedEventHandlers) { + eventHandlers.remove(zombie) + val temp5 = mFirstEventHandler + if (temp5 != null && zombie.equals(temp5.first)) { + mFirstEventHandler = null } } - mToBeRemovedEventHandlers.clear(); + mToBeRemovedEventHandlers.clear() } // Add new handlers if (mToBeAddedFirstEventHandler != null) { - mFirstEventHandler = mToBeAddedFirstEventHandler; - mToBeAddedFirstEventHandler = null; + mFirstEventHandler = mToBeAddedFirstEventHandler + mToBeAddedFirstEventHandler = null } - if (mToBeAddedEventHandlers.size() > 0) { - for (Entry food : mToBeAddedEventHandlers.entrySet()) { - eventHandlers.put(food.getKey(), food.getValue()); + if (mToBeAddedEventHandlers.size > 0) { + for (food in mToBeAddedEventHandlers.entries) { + eventHandlers.put(food.key, food.value) } } } @@ -563,151 +565,179 @@ public class CalendarController { * @param key The view id or placeholder for this handler * @param eventHandler Typically a fragment or activity in the calendar app */ - public void registerEventHandler(int key, EventHandler eventHandler) { - synchronized (this) { + fun registerEventHandler(key: Integer, eventHandler: EventHandler?) { + synchronized(this) { if (mDispatchInProgressCounter > 0) { - mToBeAddedEventHandlers.put(key, eventHandler); + mToBeAddedEventHandlers.put(key, + eventHandler as CalendarController.EventHandler) } else { - eventHandlers.put(key, eventHandler); + eventHandlers.put(key, eventHandler as CalendarController.EventHandler) } } } - public void registerFirstEventHandler(int key, EventHandler eventHandler) { - synchronized (this) { - registerEventHandler(key, eventHandler); + fun registerFirstEventHandler(key: Integer, eventHandler: EventHandler?) { + synchronized(this) { + registerEventHandler(key, eventHandler) if (mDispatchInProgressCounter > 0) { - mToBeAddedFirstEventHandler = new Pair(key, eventHandler); + mToBeAddedFirstEventHandler = Pair(key, eventHandler) } else { - mFirstEventHandler = new Pair(key, eventHandler); + mFirstEventHandler = Pair(key, eventHandler) } } } - public void deregisterEventHandler(Integer key) { - synchronized (this) { + fun deregisterEventHandler(key: Integer) { + synchronized(this) { if (mDispatchInProgressCounter > 0) { // To avoid ConcurrencyException, stash away the event handler for now. - mToBeRemovedEventHandlers.add(key); + mToBeRemovedEventHandlers.add(key) } else { - eventHandlers.remove(key); - if (mFirstEventHandler != null && mFirstEventHandler.first == key) { - mFirstEventHandler = null; - } + eventHandlers.remove(key) + val temp6 = mFirstEventHandler + if (temp6 != null && temp6.first == key) { + mFirstEventHandler = null + } else {} } } } - public void deregisterAllEventHandlers() { - synchronized (this) { + fun deregisterAllEventHandlers() { + synchronized(this) { if (mDispatchInProgressCounter > 0) { // To avoid ConcurrencyException, stash away the event handler for now. - mToBeRemovedEventHandlers.addAll(eventHandlers.keySet()); + mToBeRemovedEventHandlers.addAll(eventHandlers.keys) } else { - eventHandlers.clear(); - mFirstEventHandler = null; + eventHandlers.clear() + mFirstEventHandler = null } } } // FRAG_TODO doesn't work yet - public void filterBroadcasts(Object sender, long eventTypes) { - filters.put(sender, eventTypes); + fun filterBroadcasts(sender: Object?, eventTypes: Long) { + filters.put(sender, eventTypes) } - /** * @return the time that this controller is currently pointed at */ - public long getTime() { - return mTime.toMillis(false); - } - - /** - * @return the last set of date flags sent with - * {@link EventType#UPDATE_TITLE} - */ - public long getDateFlags() { - return mDateFlags; - } - /** * Set the time this controller is currently pointed at * * @param millisTime Time since epoch in millis */ - public void setTime(long millisTime) { - mTime.set(millisTime); - } + var time: Long? + get() = mTime?.toMillis(false) + set(millisTime) { + mTime?.set(millisTime as Long) + } - /** - * @return the last event ID the edit view was launched with - */ - public long getEventId() { - return mEventId; + fun launchViewEvent(eventId: Long, startMillis: Long, endMillis: Long, response: Int) { + val intent = Intent(Intent.ACTION_VIEW) + val eventUri: Uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId) + intent.setData(eventUri) + intent.setClass(mContext as Context, AllInOneActivity::class.java) + intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis) + intent.putExtra(EXTRA_EVENT_END_TIME, endMillis) + intent.putExtra(ATTENDEE_STATUS, response) + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + mContext?.startActivity(intent) } - public int getViewType() { - return mViewType; + private fun eventInfoToString(eventInfo: EventInfo): String { + var tmp = "Unknown" + val builder = StringBuilder() + if (eventInfo.eventType and EventType.GO_TO != 0L) { + tmp = "Go to time/event" + } else if (eventInfo.eventType and EventType.VIEW_EVENT != 0L) { + tmp = "View event" + } else if (eventInfo.eventType and EventType.VIEW_EVENT_DETAILS != 0L) { + tmp = "View details" + } else if (eventInfo.eventType and EventType.EVENTS_CHANGED != 0L) { + tmp = "Refresh events" + } else if (eventInfo.eventType and EventType.USER_HOME != 0L) { + tmp = "Gone home" + } else if (eventInfo.eventType and EventType.UPDATE_TITLE != 0L) { + tmp = "Update title" + } + builder.append(tmp) + builder.append(": id=") + builder.append(eventInfo.id) + builder.append(", selected=") + builder.append(eventInfo.selectedTime) + builder.append(", start=") + builder.append(eventInfo.startTime) + builder.append(", end=") + builder.append(eventInfo.endTime) + builder.append(", viewType=") + builder.append(eventInfo.viewType) + builder.append(", x=") + builder.append(eventInfo.x) + builder.append(", y=") + builder.append(eventInfo.y) + return builder.toString() } - public int getPreviousViewType() { - return mPreviousViewType; - } + companion object { + private const val DEBUG = false + private const val TAG = "CalendarController" + const val EVENT_EDIT_ON_LAUNCH = "editMode" + const val MIN_CALENDAR_YEAR = 1970 + const val MAX_CALENDAR_YEAR = 2036 + const val MIN_CALENDAR_WEEK = 0 + const val MAX_CALENDAR_WEEK = 3497 // weeks between 1/1/1970 and 1/1/2037 + private val instances: WeakHashMap> = + WeakHashMap>() - public void launchViewEvent(long eventId, long startMillis, long endMillis, int response) { - Intent intent = new Intent(Intent.ACTION_VIEW); - Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId); - intent.setData(eventUri); - intent.setClass(mContext, AllInOneActivity.class); - intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis); - intent.putExtra(EXTRA_EVENT_END_TIME, endMillis); - intent.putExtra(ATTENDEE_STATUS, response); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivity(intent); - } + /** + * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time + * can be ignored + */ + const val EXTRA_GOTO_DATE: Long = 1 + const val EXTRA_GOTO_TIME: Long = 2 + const val EXTRA_GOTO_BACK_TO_PREVIOUS: Long = 4 + const val EXTRA_GOTO_TODAY: Long = 8 - // Forces the viewType. Should only be used for initialization. - public void setViewType(int viewType) { - mViewType = viewType; - } + /** + * Creates and/or returns an instance of CalendarController associated with + * the supplied context. It is best to pass in the current Activity. + * + * @param context The activity if at all possible. + */ + @JvmStatic fun getInstance(context: Context): CalendarController { + synchronized(instances) { + var controller: CalendarController? = null + val weakController: WeakReference? = instances.get(context) + if (weakController != null) { + controller = weakController.get() + } + if (controller == null) { + controller = CalendarController(context) + instances.put(context, WeakReference(controller)) + } + return controller + } + } - // Sets the eventId. Should only be used for initialization. - public void setEventId(long eventId) { - mEventId = eventId; + /** + * Removes an instance when it is no longer needed. This should be called in + * an activity's onDestroy method. + * + * @param context The activity used to create the controller + */ + @JvmStatic fun removeInstance(context: Context?) { + instances.remove(context) + } } - private String eventInfoToString(EventInfo eventInfo) { - String tmp = "Unknown"; - - StringBuilder builder = new StringBuilder(); - if ((eventInfo.eventType & EventType.GO_TO) != 0) { - tmp = "Go to time/event"; - } else if ((eventInfo.eventType & EventType.VIEW_EVENT) != 0) { - tmp = "View event"; - } else if ((eventInfo.eventType & EventType.VIEW_EVENT_DETAILS) != 0) { - tmp = "View details"; - } else if ((eventInfo.eventType & EventType.EVENTS_CHANGED) != 0) { - tmp = "Refresh events"; - } else if ((eventInfo.eventType & EventType.USER_HOME) != 0) { - tmp = "Gone home"; - } else if ((eventInfo.eventType & EventType.UPDATE_TITLE) != 0) { - tmp = "Update title"; - } - builder.append(tmp); - builder.append(": id="); - builder.append(eventInfo.id); - builder.append(", selected="); - builder.append(eventInfo.selectedTime); - builder.append(", start="); - builder.append(eventInfo.startTime); - builder.append(", end="); - builder.append(eventInfo.endTime); - builder.append(", viewType="); - builder.append(eventInfo.viewType); - builder.append(", x="); - builder.append(eventInfo.x); - builder.append(", y="); - builder.append(eventInfo.y); - return builder.toString(); + init { + mContext = context + mUpdateTimezone.run() + mTime?.setToNow() + mDetailViewType = Utils.getSharedPreference( + mContext, + GeneralPreferences.KEY_DETAILED_VIEW, + GeneralPreferences.DEFAULT_DETAILED_VIEW + ) } -} +} \ No newline at end of file -- cgit v1.2.3 From c201c3495ad721a86c60df192e4c7cb52a0cec96 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Mon, 21 Jun 2021 20:45:28 +0000 Subject: AOSP/Calendar - Add Kotlin code for GeneralPreferences.kt. Also uploaded corresponding Android.bp file I added @JvmStatic annotations to the static methods in the companion object. This error in conversion has been noted previously in the CalendarUtils.kt file conversion. Test: manual (ran build and unit tests) and CTS testing suite $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.203 OK (22 tests) CTS TESTING: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ============================================ ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 17m 52s Total aggregated tests run time: 17m 52s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.35 tests/sec [376 tests / 1072892 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 10539 ms || clean = 2601 ms Total preparation time: 10s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 20m 30s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: I3292512216fe63420d1eeb7ff6fab9bf93970b54 --- Android.bp | 1 + src/com/android/calendar/GeneralPreferences.kt | 545 ++++++++++++------------- 2 files changed, 264 insertions(+), 282 deletions(-) diff --git a/Android.bp b/Android.bp index 99d07cf3..d3aa41c8 100644 --- a/Android.bp +++ b/Android.bp @@ -37,6 +37,7 @@ exclude_srcsm = [ "src/**/calendar/CalendarBackupAgent.java", "src/**/calendar/CalendarUtils.java", "src/**/calendar/EventGeometry.java", + "src/**/calendar/GeneralPreferences.java", "src/**/calendar/GoogleCalendarUriIntentFilter.java", "src/**/calendar/MultiStateButton.java", "src/**/calendar/OtherPreferences.java", diff --git a/src/com/android/calendar/GeneralPreferences.kt b/src/com/android/calendar/GeneralPreferences.kt index a42f07e3..259a8918 100644 --- a/src/com/android/calendar/GeneralPreferences.kt +++ b/src/com/android/calendar/GeneralPreferences.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,274 +13,186 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar; - -import android.app.Activity; -import android.app.FragmentManager; -import android.app.backup.BackupManager; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.media.Ringtone; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.Vibrator; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceCategory; -import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.preference.RingtonePreference; -import android.provider.CalendarContract; -import android.provider.CalendarContract.CalendarCache; -import android.provider.SearchRecentSuggestions; -import android.text.TextUtils; -import android.text.format.Time; -import android.widget.Toast; - -import com.android.calendar.alerts.AlertReceiver; -import com.android.timezonepicker.TimeZoneInfo; -import com.android.timezonepicker.TimeZonePickerDialog; -import com.android.timezonepicker.TimeZonePickerDialog.OnTimeZoneSetListener; -import com.android.timezonepicker.TimeZonePickerUtils; - -public class GeneralPreferences extends PreferenceFragment implements - OnSharedPreferenceChangeListener, OnPreferenceChangeListener, OnTimeZoneSetListener { - // The name of the shared preferences file. This name must be maintained for historical - // reasons, as it's what PreferenceManager assigned the first time the file was created. - static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; - static final String SHARED_PREFS_NAME_NO_BACKUP = "com.android.calendar_preferences_no_backup"; - - private static final String FRAG_TAG_TIME_ZONE_PICKER = "TimeZonePicker"; - - // Preference keys - public static final String KEY_HIDE_DECLINED = "preferences_hide_declined"; - public static final String KEY_WEEK_START_DAY = "preferences_week_start_day"; - public static final String KEY_SHOW_WEEK_NUM = "preferences_show_week_num"; - public static final String KEY_DAYS_PER_WEEK = "preferences_days_per_week"; - public static final String KEY_SKIP_SETUP = "preferences_skip_setup"; - - public static final String KEY_CLEAR_SEARCH_HISTORY = "preferences_clear_search_history"; - - public static final String KEY_ALERTS_CATEGORY = "preferences_alerts_category"; - public static final String KEY_ALERTS = "preferences_alerts"; - public static final String KEY_ALERTS_VIBRATE = "preferences_alerts_vibrate"; - public static final String KEY_ALERTS_RINGTONE = "preferences_alerts_ringtone"; - public static final String KEY_ALERTS_POPUP = "preferences_alerts_popup"; - - public static final String KEY_SHOW_CONTROLS = "preferences_show_controls"; - - public static final String KEY_DEFAULT_REMINDER = "preferences_default_reminder"; - public static final int NO_REMINDER = -1; - public static final String NO_REMINDER_STRING = "-1"; - public static final int REMINDER_DEFAULT_TIME = 10; // in minutes - - public static final String KEY_DEFAULT_CELL_HEIGHT = "preferences_default_cell_height"; - public static final String KEY_VERSION = "preferences_version"; - - /** Key to SharePreference for default view (CalendarController.ViewType) */ - public static final String KEY_START_VIEW = "preferred_startView"; - /** - * Key to SharePreference for default detail view (CalendarController.ViewType) - * Typically used by widget - */ - public static final String KEY_DETAILED_VIEW = "preferred_detailedView"; - public static final String KEY_DEFAULT_CALENDAR = "preference_defaultCalendar"; - - // These must be in sync with the array preferences_week_start_day_values - public static final String WEEK_START_DEFAULT = "-1"; - public static final String WEEK_START_SATURDAY = "7"; - public static final String WEEK_START_SUNDAY = "1"; - public static final String WEEK_START_MONDAY = "2"; - - // These keys are kept to enable migrating users from previous versions - private static final String KEY_ALERTS_TYPE = "preferences_alerts_type"; - private static final String ALERT_TYPE_ALERTS = "0"; - private static final String ALERT_TYPE_STATUS_BAR = "1"; - private static final String ALERT_TYPE_OFF = "2"; - static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled"; - static final String KEY_HOME_TZ = "preferences_home_tz"; - - // Default preference values - public static final int DEFAULT_START_VIEW = CalendarController.ViewType.WEEK; - public static final int DEFAULT_DETAILED_VIEW = CalendarController.ViewType.DAY; - public static final boolean DEFAULT_SHOW_WEEK_NUM = false; - // This should match the XML file. - public static final String DEFAULT_RINGTONE = "content://settings/system/notification_sound"; - - CheckBoxPreference mAlert; - CheckBoxPreference mVibrate; - CheckBoxPreference mPopup; - CheckBoxPreference mUseHomeTZ; - CheckBoxPreference mHideDeclined; - Preference mHomeTZ; - TimeZonePickerUtils mTzPickerUtils; - ListPreference mWeekStart; - ListPreference mDefaultReminder; - - private String mTimeZoneId; - - /** Return a properly configured SharedPreferences instance */ - public static SharedPreferences getSharedPreferences(Context context) { - return context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - } - - /** Set the default shared preferences in the proper context */ - public static void setDefaultValues(Context context) { - PreferenceManager.setDefaultValues(context, SHARED_PREFS_NAME, Context.MODE_PRIVATE, - R.xml.general_preferences, false); - } +package com.android.calendar + +import android.app.Activity +import android.app.FragmentManager +import android.app.backup.BackupManager +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.content.SharedPreferences.Editor +import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import android.media.Ringtone +import android.media.RingtoneManager +import android.net.Uri +import android.os.Bundle +import android.os.Vibrator +import android.preference.CheckBoxPreference +import android.preference.ListPreference +import android.preference.Preference +import android.preference.Preference.OnPreferenceChangeListener +import android.preference.Preference.OnPreferenceClickListener +import android.preference.PreferenceCategory +import android.preference.PreferenceFragment +import android.preference.PreferenceManager +import android.preference.PreferenceScreen +import android.preference.RingtonePreference +import android.provider.CalendarContract +import android.provider.CalendarContract.CalendarCache +import android.provider.SearchRecentSuggestions +import android.text.TextUtils +import android.text.format.Time +import android.widget.Toast +import com.android.calendar.alerts.AlertReceiver +import com.android.timezonepicker.TimeZoneInfo +import com.android.timezonepicker.TimeZonePickerDialog +import com.android.timezonepicker.TimeZonePickerDialog.OnTimeZoneSetListener +import com.android.timezonepicker.TimeZonePickerUtils + +class GeneralPreferences : PreferenceFragment(), OnSharedPreferenceChangeListener, + OnPreferenceChangeListener, OnTimeZoneSetListener { + var mAlert: CheckBoxPreference? = null + var mVibrate: CheckBoxPreference? = null + var mPopup: CheckBoxPreference? = null + var mUseHomeTZ: CheckBoxPreference? = null + var mHideDeclined: CheckBoxPreference? = null + var mHomeTZ: Preference? = null + var mTzPickerUtils: TimeZonePickerUtils? = null + var mWeekStart: ListPreference? = null + var mDefaultReminder: ListPreference? = null + private var mTimeZoneId: String? = null @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - final Activity activity = getActivity(); + override fun onCreate(icicle: Bundle?) { + super.onCreate(icicle) + val activity: Activity = getActivity() // Make sure to always use the same preferences file regardless of the package name // we're running under - final PreferenceManager preferenceManager = getPreferenceManager(); - final SharedPreferences sharedPreferences = getSharedPreferences(activity); - preferenceManager.setSharedPreferencesName(SHARED_PREFS_NAME); + val preferenceManager: PreferenceManager = getPreferenceManager() + val sharedPreferences: SharedPreferences = getSharedPreferences(activity) + preferenceManager.setSharedPreferencesName(SHARED_PREFS_NAME) // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.general_preferences); - - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - mAlert = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS); - mVibrate = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS_VIBRATE); - Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); + addPreferencesFromResource(R.xml.general_preferences) + val preferenceScreen: PreferenceScreen = getPreferenceScreen() + mAlert = preferenceScreen.findPreference(KEY_ALERTS) as CheckBoxPreference + mVibrate = preferenceScreen.findPreference(KEY_ALERTS_VIBRATE) as CheckBoxPreference + val vibrator: Vibrator = activity.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator if (vibrator == null || !vibrator.hasVibrator()) { - PreferenceCategory mAlertGroup = (PreferenceCategory) preferenceScreen - .findPreference(KEY_ALERTS_CATEGORY); - mAlertGroup.removePreference(mVibrate); + val mAlertGroup: PreferenceCategory = preferenceScreen + .findPreference(KEY_ALERTS_CATEGORY) as PreferenceCategory + mAlertGroup.removePreference(mVibrate) } - - mPopup = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS_POPUP); - mUseHomeTZ = (CheckBoxPreference) preferenceScreen.findPreference(KEY_HOME_TZ_ENABLED); - mHideDeclined = (CheckBoxPreference) preferenceScreen.findPreference(KEY_HIDE_DECLINED); - mWeekStart = (ListPreference) preferenceScreen.findPreference(KEY_WEEK_START_DAY); - mDefaultReminder = (ListPreference) preferenceScreen.findPreference(KEY_DEFAULT_REMINDER); - mHomeTZ = preferenceScreen.findPreference(KEY_HOME_TZ); - mWeekStart.setSummary(mWeekStart.getEntry()); - mDefaultReminder.setSummary(mDefaultReminder.getEntry()); + mPopup = preferenceScreen.findPreference(KEY_ALERTS_POPUP) as CheckBoxPreference + mUseHomeTZ = preferenceScreen.findPreference(KEY_HOME_TZ_ENABLED) as CheckBoxPreference + mHideDeclined = preferenceScreen.findPreference(KEY_HIDE_DECLINED) as CheckBoxPreference + mWeekStart = preferenceScreen.findPreference(KEY_WEEK_START_DAY) as ListPreference + mDefaultReminder = preferenceScreen.findPreference(KEY_DEFAULT_REMINDER) as ListPreference + mHomeTZ = preferenceScreen.findPreference(KEY_HOME_TZ) + mWeekStart?.setSummary(mWeekStart?.getEntry()) + mDefaultReminder?.setSummary(mDefaultReminder?.getEntry()) // This triggers an asynchronous call to the provider to refresh the data in shared pref - mTimeZoneId = Utils.getTimeZone(activity, null); - - SharedPreferences prefs = CalendarUtils.getSharedPreferences(activity, - Utils.SHARED_PREFS_NAME); + mTimeZoneId = Utils.getTimeZone(activity, null) + val prefs: SharedPreferences = CalendarUtils.getSharedPreferences(activity, + Utils.SHARED_PREFS_NAME) // Utils.getTimeZone will return the currentTimeZone instead of the one // in the shared_pref if home time zone is disabled. So if home tz is // off, we will explicitly read it. if (!prefs.getBoolean(KEY_HOME_TZ_ENABLED, false)) { - mTimeZoneId = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()); + mTimeZoneId = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()) } - mHomeTZ.setOnPreferenceClickListener(new OnPreferenceClickListener() { + mHomeTZ?.setOnPreferenceClickListener(object: Preference.OnPreferenceClickListener { @Override - public boolean onPreferenceClick(Preference preference) { - showTimezoneDialog(); - return true; + override fun onPreferenceClick(preference: Preference?): Boolean { + showTimezoneDialog() + return true } - }); + }) if (mTzPickerUtils == null) { - mTzPickerUtils = new TimeZonePickerUtils(getActivity()); + mTzPickerUtils = TimeZonePickerUtils(getActivity()) } - CharSequence timezoneName = mTzPickerUtils.getGmtDisplayName(getActivity(), mTimeZoneId, - System.currentTimeMillis(), false); - mHomeTZ.setSummary(timezoneName != null ? timezoneName : mTimeZoneId); - - TimeZonePickerDialog tzpd = (TimeZonePickerDialog) activity.getFragmentManager() - .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER); + val timezoneName: CharSequence? = mTzPickerUtils?.getGmtDisplayName(getActivity(), + mTimeZoneId, System.currentTimeMillis(), false) + mHomeTZ?.setSummary(timezoneName ?: mTimeZoneId) + val tzpd: TimeZonePickerDialog = activity.getFragmentManager() + .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER) as TimeZonePickerDialog if (tzpd != null) { - tzpd.setOnTimeZoneSetListener(this); + tzpd.setOnTimeZoneSetListener(this) } - - migrateOldPreferences(sharedPreferences); - - updateChildPreferences(); + migrateOldPreferences(sharedPreferences) + updateChildPreferences() } - private void showTimezoneDialog() { - final Activity activity = getActivity(); - if (activity == null) { - return; - } - - Bundle b = new Bundle(); - b.putLong(TimeZonePickerDialog.BUNDLE_START_TIME_MILLIS, System.currentTimeMillis()); - b.putString(TimeZonePickerDialog.BUNDLE_TIME_ZONE, Utils.getTimeZone(activity, null)); - - FragmentManager fm = getActivity().getFragmentManager(); - TimeZonePickerDialog tzpd = (TimeZonePickerDialog) fm - .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER); + private fun showTimezoneDialog() { + val activity: Activity = getActivity() ?: return + val b = Bundle() + b.putLong(TimeZonePickerDialog.BUNDLE_START_TIME_MILLIS, System.currentTimeMillis()) + b.putString(TimeZonePickerDialog.BUNDLE_TIME_ZONE, Utils.getTimeZone(activity, null)) + val fm: FragmentManager = getActivity().getFragmentManager() + var tzpd: TimeZonePickerDialog? = fm + .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER) as TimeZonePickerDialog if (tzpd != null) { - tzpd.dismiss(); + tzpd.dismiss() } - tzpd = new TimeZonePickerDialog(); - tzpd.setArguments(b); - tzpd.setOnTimeZoneSetListener(this); - tzpd.show(fm, FRAG_TAG_TIME_ZONE_PICKER); + tzpd = TimeZonePickerDialog() + tzpd.setArguments(b) + tzpd.setOnTimeZoneSetListener(this) + tzpd.show(fm, FRAG_TAG_TIME_ZONE_PICKER) } @Override - public void onStart() { - super.onStart(); + override fun onStart() { + super.onStart() getPreferenceScreen().getSharedPreferences() - .registerOnSharedPreferenceChangeListener(this); - setPreferenceListeners(this); + .registerOnSharedPreferenceChangeListener(this) + setPreferenceListeners(this) } /** * Sets up all the preference change listeners to use the specified * listener. */ - private void setPreferenceListeners(OnPreferenceChangeListener listener) { - mUseHomeTZ.setOnPreferenceChangeListener(listener); - mHomeTZ.setOnPreferenceChangeListener(listener); - mWeekStart.setOnPreferenceChangeListener(listener); - mDefaultReminder.setOnPreferenceChangeListener(listener); - mHideDeclined.setOnPreferenceChangeListener(listener); - mVibrate.setOnPreferenceChangeListener(listener); + private fun setPreferenceListeners(listener: OnPreferenceChangeListener?) { + mUseHomeTZ?.setOnPreferenceChangeListener(listener) + mHomeTZ?.setOnPreferenceChangeListener(listener) + mWeekStart?.setOnPreferenceChangeListener(listener) + mDefaultReminder?.setOnPreferenceChangeListener(listener) + mHideDeclined?.setOnPreferenceChangeListener(listener) + mVibrate?.setOnPreferenceChangeListener(listener) } @Override - public void onStop() { + override fun onStop() { getPreferenceScreen().getSharedPreferences() - .unregisterOnSharedPreferenceChangeListener(this); - setPreferenceListeners(null); - super.onStop(); + .unregisterOnSharedPreferenceChangeListener(this) + setPreferenceListeners(null) + super.onStop() } @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - Activity a = getActivity(); + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String) { + val a: Activity = getActivity() if (key.equals(KEY_ALERTS)) { - updateChildPreferences(); + updateChildPreferences() if (a != null) { - Intent intent = new Intent(); - intent.setClass(a, AlertReceiver.class); - if (mAlert.isChecked()) { - intent.setAction(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS); + val intent = Intent() + intent.setClass(a, AlertReceiver::class.java) + if (mAlert?.isChecked() ?: false) { + intent.setAction(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS) } else { - intent.setAction(AlertReceiver.EVENT_REMINDER_APP_ACTION); + intent.setAction(AlertReceiver.EVENT_REMINDER_APP_ACTION) } - a.sendBroadcast(intent); + a.sendBroadcast(intent) } } if (a != null) { - BackupManager.dataChanged(a.getPackageName()); + BackupManager.dataChanged(a.getPackageName()) } } @@ -288,48 +200,46 @@ public class GeneralPreferences extends PreferenceFragment implements * Handles time zone preference changes */ @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String tz; - final Activity activity = getActivity(); - if (preference == mUseHomeTZ) { - if ((Boolean)newValue) { - tz = mTimeZoneId; + override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { + val tz: String? + val activity: Activity = getActivity() + if (preference === mUseHomeTZ) { + tz = if (newValue != null) { + mTimeZoneId } else { - tz = CalendarCache.TIMEZONE_TYPE_AUTO; + CalendarCache.TIMEZONE_TYPE_AUTO } - Utils.setTimeZone(activity, tz); - return true; - } else if (preference == mHideDeclined) { - mHideDeclined.setChecked((Boolean) newValue); - Intent intent = new Intent(Utils.getWidgetScheduledUpdateAction(activity)); - intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE); - activity.sendBroadcast(intent); - return true; - } else if (preference == mWeekStart) { - mWeekStart.setValue((String) newValue); - mWeekStart.setSummary(mWeekStart.getEntry()); - } else if (preference == mDefaultReminder) { - mDefaultReminder.setValue((String) newValue); - mDefaultReminder.setSummary(mDefaultReminder.getEntry()); - } else if (preference == mVibrate) { - mVibrate.setChecked((Boolean) newValue); - return true; + Utils.setTimeZone(activity, tz) + return true + } else if (preference === mHideDeclined) { + mHideDeclined?.setChecked(newValue as Boolean) + val intent = Intent(Utils.getWidgetScheduledUpdateAction(activity)) + intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE) + activity.sendBroadcast(intent) + return true + } else if (preference === mWeekStart) { + mWeekStart?.setValue(newValue as String) + mWeekStart?.setSummary(mWeekStart?.getEntry()) + } else if (preference === mDefaultReminder) { + mDefaultReminder?.setValue(newValue as String) + mDefaultReminder?.setSummary(mDefaultReminder?.getEntry()) + } else if (preference === mVibrate) { + mVibrate?.setChecked(newValue as Boolean) + return true } else { - return true; + return true } - return false; + return false } - public String getRingtoneTitleFromUri(Context context, String uri) { + fun getRingtoneTitleFromUri(context: Context?, uri: String?): String? { if (TextUtils.isEmpty(uri)) { - return null; - } - - Ringtone ring = RingtoneManager.getRingtone(getActivity(), Uri.parse(uri)); - if (ring != null) { - return ring.getTitle(context); + return null } - return null; + val ring: Ringtone = RingtoneManager.getRingtone(getActivity(), Uri.parse(uri)) + return if (ring != null) { + ring.getTitle(context) + } else null } /** @@ -337,29 +247,28 @@ public class GeneralPreferences extends PreferenceFragment implements * set of keys and values. * @param prefs the preferences to upgrade */ - private void migrateOldPreferences(SharedPreferences prefs) { + private fun migrateOldPreferences(prefs: SharedPreferences) { // If needed, migrate vibration setting from a previous version - - mVibrate.setChecked(Utils.getDefaultVibrate(getActivity(), prefs)); + mVibrate?.setChecked(Utils.getDefaultVibrate(getActivity(), prefs)) // If needed, migrate the old alerts type settin if (!prefs.contains(KEY_ALERTS) && prefs.contains(KEY_ALERTS_TYPE)) { - String type = prefs.getString(KEY_ALERTS_TYPE, ALERT_TYPE_STATUS_BAR); + val type: String? = prefs.getString(KEY_ALERTS_TYPE, ALERT_TYPE_STATUS_BAR) if (type.equals(ALERT_TYPE_OFF)) { - mAlert.setChecked(false); - mPopup.setChecked(false); - mPopup.setEnabled(false); + mAlert?.setChecked(false) + mPopup?.setChecked(false) + mPopup?.setEnabled(false) } else if (type.equals(ALERT_TYPE_STATUS_BAR)) { - mAlert.setChecked(true); - mPopup.setChecked(false); - mPopup.setEnabled(true); + mAlert?.setChecked(true) + mPopup?.setChecked(false) + mPopup?.setEnabled(true) } else if (type.equals(ALERT_TYPE_ALERTS)) { - mAlert.setChecked(true); - mPopup.setChecked(true); - mPopup.setEnabled(true); + mAlert?.setChecked(true) + mPopup?.setChecked(true) + mPopup?.setEnabled(true) } // clear out the old setting - prefs.edit().remove(KEY_ALERTS_TYPE).commit(); + prefs.edit().remove(KEY_ALERTS_TYPE).commit() } } @@ -368,33 +277,105 @@ public class GeneralPreferences extends PreferenceFragment implements * example, when notifications are turned off, we disable the preferences * for configuring the exact notification behavior. */ - private void updateChildPreferences() { - if (mAlert.isChecked()) { - mVibrate.setEnabled(true); - mPopup.setEnabled(true); + private fun updateChildPreferences() { + if (mAlert?.isChecked() ?: false) { + mVibrate?.setEnabled(true) + mPopup?.setEnabled(true) } else { - mVibrate.setEnabled(false); - mPopup.setEnabled(false); + mVibrate?.setEnabled(false) + mPopup?.setEnabled(false) } } - @Override - public boolean onPreferenceTreeClick( - PreferenceScreen preferenceScreen, Preference preference) { - final String key = preference.getKey(); - return super.onPreferenceTreeClick(preferenceScreen, preference); + override fun onPreferenceTreeClick( + preferenceScreen: PreferenceScreen?, preference: Preference): Boolean { + val key: String = preference.getKey() + return super.onPreferenceTreeClick(preferenceScreen, preference) } @Override - public void onTimeZoneSet(TimeZoneInfo tzi) { + override fun onTimeZoneSet(tzi: TimeZoneInfo) { if (mTzPickerUtils == null) { - mTzPickerUtils = new TimeZonePickerUtils(getActivity()); + mTzPickerUtils = TimeZonePickerUtils(getActivity()) + } + val timezoneName: CharSequence? = mTzPickerUtils?.getGmtDisplayName( + getActivity(), tzi.mTzId, System.currentTimeMillis(), false) + mHomeTZ?.setSummary(timezoneName) + Utils.setTimeZone(getActivity(), tzi.mTzId) + } + + companion object { + // The name of the shared preferences file. This name must be maintained for historical + // reasons, as it's what PreferenceManager assigned the first time the file was created. + const val SHARED_PREFS_NAME = "com.android.calendar_preferences" + const val SHARED_PREFS_NAME_NO_BACKUP = "com.android.calendar_preferences_no_backup" + private const val FRAG_TAG_TIME_ZONE_PICKER = "TimeZonePicker" + + // Preference keys + const val KEY_HIDE_DECLINED = "preferences_hide_declined" + const val KEY_WEEK_START_DAY = "preferences_week_start_day" + const val KEY_SHOW_WEEK_NUM = "preferences_show_week_num" + const val KEY_DAYS_PER_WEEK = "preferences_days_per_week" + const val KEY_SKIP_SETUP = "preferences_skip_setup" + const val KEY_CLEAR_SEARCH_HISTORY = "preferences_clear_search_history" + const val KEY_ALERTS_CATEGORY = "preferences_alerts_category" + const val KEY_ALERTS = "preferences_alerts" + const val KEY_ALERTS_VIBRATE = "preferences_alerts_vibrate" + const val KEY_ALERTS_RINGTONE = "preferences_alerts_ringtone" + const val KEY_ALERTS_POPUP = "preferences_alerts_popup" + const val KEY_SHOW_CONTROLS = "preferences_show_controls" + const val KEY_DEFAULT_REMINDER = "preferences_default_reminder" + const val NO_REMINDER = -1 + const val NO_REMINDER_STRING = "-1" + const val REMINDER_DEFAULT_TIME = 10 // in minutes + const val KEY_DEFAULT_CELL_HEIGHT = "preferences_default_cell_height" + const val KEY_VERSION = "preferences_version" + + /** Key to SharePreference for default view (CalendarController.ViewType) */ + const val KEY_START_VIEW = "preferred_startView" + + /** + * Key to SharePreference for default detail view (CalendarController.ViewType) + * Typically used by widget + */ + const val KEY_DETAILED_VIEW = "preferred_detailedView" + const val KEY_DEFAULT_CALENDAR = "preference_defaultCalendar" + + // These must be in sync with the array preferences_week_start_day_values + const val WEEK_START_DEFAULT = "-1" + const val WEEK_START_SATURDAY = "7" + const val WEEK_START_SUNDAY = "1" + const val WEEK_START_MONDAY = "2" + + // These keys are kept to enable migrating users from previous versions + private const val KEY_ALERTS_TYPE = "preferences_alerts_type" + private const val ALERT_TYPE_ALERTS = "0" + private const val ALERT_TYPE_STATUS_BAR = "1" + private const val ALERT_TYPE_OFF = "2" + const val KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled" + const val KEY_HOME_TZ = "preferences_home_tz" + + // Default preference values + const val DEFAULT_START_VIEW: Int = CalendarController.ViewType.WEEK + const val DEFAULT_DETAILED_VIEW: Int = CalendarController.ViewType.DAY + const val DEFAULT_SHOW_WEEK_NUM = false + + // This should match the XML file. + const val DEFAULT_RINGTONE = "content://settings/system/notification_sound" + + + /** Return a properly configured SharedPreferences instance */ + @JvmStatic + fun getSharedPreferences(context: Context): SharedPreferences { + return context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) } - final CharSequence timezoneName = mTzPickerUtils.getGmtDisplayName( - getActivity(), tzi.mTzId, System.currentTimeMillis(), false); - mHomeTZ.setSummary(timezoneName); - Utils.setTimeZone(getActivity(), tzi.mTzId); + /** Set the default shared preferences in the proper context */ + @JvmStatic + fun setDefaultValues(context: Context?) { + PreferenceManager.setDefaultValues(context, SHARED_PREFS_NAME, Context.MODE_PRIVATE, + R.xml.general_preferences, false) + } } -} +} \ No newline at end of file -- cgit v1.2.3 From c49f19b9ee91897b7c60d924542aff6a82762f28 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Mon, 21 Jun 2021 22:40:32 +0000 Subject: AOSP/Calendar - Add Kotlin copy of EventLoader.java Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: I689941c0ac3f9fc398354365cbbfeceb8ec3a34c --- src/com/android/calendar/EventLoader.kt | 286 ++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 src/com/android/calendar/EventLoader.kt diff --git a/src/com/android/calendar/EventLoader.kt b/src/com/android/calendar/EventLoader.kt new file mode 100644 index 00000000..d34b1c7c --- /dev/null +++ b/src/com/android/calendar/EventLoader.kt @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2008 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.os.Handler; +import android.os.Process; +import android.provider.CalendarContract; +import android.provider.CalendarContract.EventDays; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; + +public class EventLoader { + + private Context mContext; + private Handler mHandler = new Handler(); + private AtomicInteger mSequenceNumber = new AtomicInteger(); + + private LinkedBlockingQueue mLoaderQueue; + private LoaderThread mLoaderThread; + private ContentResolver mResolver; + + private static interface LoadRequest { + public void processRequest(EventLoader eventLoader); + public void skipRequest(EventLoader eventLoader); + } + + private static class ShutdownRequest implements LoadRequest { + public void processRequest(EventLoader eventLoader) { + } + + public void skipRequest(EventLoader eventLoader) { + } + } + + /** + * + * Code for handling requests to get whether days have an event or not + * and filling in the eventDays array. + * + */ + private static class LoadEventDaysRequest implements LoadRequest { + public int startDay; + public int numDays; + public boolean[] eventDays; + public Runnable uiCallback; + + /** + * The projection used by the EventDays query. + */ + private static final String[] PROJECTION = { + CalendarContract.EventDays.STARTDAY, CalendarContract.EventDays.ENDDAY + }; + + public LoadEventDaysRequest(int startDay, int numDays, boolean[] eventDays, + final Runnable uiCallback) + { + this.startDay = startDay; + this.numDays = numDays; + this.eventDays = eventDays; + this.uiCallback = uiCallback; + } + + @Override + public void processRequest(EventLoader eventLoader) + { + final Handler handler = eventLoader.mHandler; + ContentResolver cr = eventLoader.mResolver; + + // Clear the event days + Arrays.fill(eventDays, false); + + //query which days have events + Cursor cursor = EventDays.query(cr, startDay, numDays, PROJECTION); + try { + int startDayColumnIndex = cursor.getColumnIndexOrThrow(EventDays.STARTDAY); + int endDayColumnIndex = cursor.getColumnIndexOrThrow(EventDays.ENDDAY); + + //Set all the days with events to true + while (cursor.moveToNext()) { + int firstDay = cursor.getInt(startDayColumnIndex); + int lastDay = cursor.getInt(endDayColumnIndex); + //we want the entire range the event occurs, but only within the month + int firstIndex = Math.max(firstDay - startDay, 0); + int lastIndex = Math.min(lastDay - startDay, 30); + + for(int i = firstIndex; i <= lastIndex; i++) { + eventDays[i] = true; + } + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + handler.post(uiCallback); + } + + @Override + public void skipRequest(EventLoader eventLoader) { + } + } + + private static class LoadEventsRequest implements LoadRequest { + + public int id; + public int startDay; + public int numDays; + public ArrayList events; + public Runnable successCallback; + public Runnable cancelCallback; + + public LoadEventsRequest(int id, int startDay, int numDays, ArrayList events, + final Runnable successCallback, final Runnable cancelCallback) { + this.id = id; + this.startDay = startDay; + this.numDays = numDays; + this.events = events; + this.successCallback = successCallback; + this.cancelCallback = cancelCallback; + } + + public void processRequest(EventLoader eventLoader) { + Event.loadEvents(eventLoader.mContext, events, startDay, + numDays, id, eventLoader.mSequenceNumber); + + // Check if we are still the most recent request. + if (id == eventLoader.mSequenceNumber.get()) { + eventLoader.mHandler.post(successCallback); + } else { + eventLoader.mHandler.post(cancelCallback); + } + } + + public void skipRequest(EventLoader eventLoader) { + eventLoader.mHandler.post(cancelCallback); + } + } + + private static class LoaderThread extends Thread { + LinkedBlockingQueue mQueue; + EventLoader mEventLoader; + + public LoaderThread(LinkedBlockingQueue queue, EventLoader eventLoader) { + mQueue = queue; + mEventLoader = eventLoader; + } + + public void shutdown() { + try { + mQueue.put(new ShutdownRequest()); + } catch (InterruptedException ex) { + // The put() method fails with InterruptedException if the + // queue is full. This should never happen because the queue + // has no limit. + Log.e("Cal", "LoaderThread.shutdown() interrupted!"); + } + } + + @Override + public void run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + while (true) { + try { + // Wait for the next request + LoadRequest request = mQueue.take(); + + // If there are a bunch of requests already waiting, then + // skip all but the most recent request. + while (!mQueue.isEmpty()) { + // Let the request know that it was skipped + request.skipRequest(mEventLoader); + + // Skip to the next request + request = mQueue.take(); + } + + if (request instanceof ShutdownRequest) { + return; + } + request.processRequest(mEventLoader); + } catch (InterruptedException ex) { + Log.e("Cal", "background LoaderThread interrupted!"); + } + } + } + } + + public EventLoader(Context context) { + mContext = context; + mLoaderQueue = new LinkedBlockingQueue(); + mResolver = context.getContentResolver(); + } + + /** + * Call this from the activity's onResume() + */ + public void startBackgroundThread() { + mLoaderThread = new LoaderThread(mLoaderQueue, this); + mLoaderThread.start(); + } + + /** + * Call this from the activity's onPause() + */ + public void stopBackgroundThread() { + mLoaderThread.shutdown(); + } + + /** + * Loads "numDays" days worth of events, starting at start, into events. + * Posts uiCallback to the {@link Handler} for this view, which will run in the UI thread. + * Reuses an existing background thread, if events were already being loaded in the background. + * NOTE: events and uiCallback are not used if an existing background thread gets reused -- + * the ones that were passed in on the call that results in the background thread getting + * created are used, and the most recent call's worth of data is loaded into events and posted + * via the uiCallback. + */ + public void loadEventsInBackground(final int numDays, final ArrayList events, + int startDay, final Runnable successCallback, final Runnable cancelCallback) { + + // Increment the sequence number for requests. We don't care if the + // sequence numbers wrap around because we test for equality with the + // latest one. + int id = mSequenceNumber.incrementAndGet(); + + // Send the load request to the background thread + LoadEventsRequest request = new LoadEventsRequest(id, startDay, numDays, + events, successCallback, cancelCallback); + + try { + mLoaderQueue.put(request); + } catch (InterruptedException ex) { + // The put() method fails with InterruptedException if the + // queue is full. This should never happen because the queue + // has no limit. + Log.e("Cal", "loadEventsInBackground() interrupted!"); + } + } + + /** + * Sends a request for the days with events to be marked. Loads "numDays" + * worth of days, starting at start, and fills in eventDays to express which + * days have events. + * + * @param startDay First day to check for events + * @param numDays Days following the start day to check + * @param eventDay Whether or not an event exists on that day + * @param uiCallback What to do when done (log data, redraw screen) + */ + void loadEventDaysInBackground(int startDay, int numDays, boolean[] eventDays, + final Runnable uiCallback) + { + // Send load request to the background thread + LoadEventDaysRequest request = new LoadEventDaysRequest(startDay, numDays, + eventDays, uiCallback); + try { + mLoaderQueue.put(request); + } catch (InterruptedException ex) { + // The put() method fails with InterruptedException if the + // queue is full. This should never happen because the queue + // has no limit. + Log.e("Cal", "loadEventDaysInBackground() interrupted!"); + } + } +} -- cgit v1.2.3 From 77f4d9d3d6771d8743d2d71cff6c9d271f43d354 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Mon, 21 Jun 2021 23:18:43 +0000 Subject: AOSP/Calendar - Add Kotlin code for EventLoader.kt. Also uploaded corresponding Android.bp file No major changes in this file. I added a few @Override annotations to follow Kotlin formatting guidelines and changed a few variable declarations. Test: manual (ran build and unit tests) and CTS testing suite $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.121 OK (22 tests) CTS TESTING: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ============================================ ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 5m 1s Total aggregated tests run time: 5m 1s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 1.25 tests/sec [376 tests / 301567 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 8579 ms || clean = 2719 ms Total preparation time: 8s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 6m 58s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: I5bd4673192e9b178e7583377ddf9e41c7a19ec8f --- Android.bp | 1 + src/com/android/calendar/EventLoader.kt | 286 +++++++++++++++----------------- 2 files changed, 133 insertions(+), 154 deletions(-) diff --git a/Android.bp b/Android.bp index d990419e..f8c7e210 100644 --- a/Android.bp +++ b/Android.bp @@ -38,6 +38,7 @@ exclude_srcsm = [ "src/**/calendar/CalendarBackupAgent.java", "src/**/calendar/CalendarUtils.java", "src/**/calendar/EventGeometry.java", + "src/**/calendar/EventLoader.java", "src/**/calendar/GeneralPreferences.java", "src/**/calendar/GoogleCalendarUriIntentFilter.java", "src/**/calendar/MultiStateButton.java", diff --git a/src/com/android/calendar/EventLoader.kt b/src/com/android/calendar/EventLoader.kt index d34b1c7c..ccfcaf15 100644 --- a/src/com/android/calendar/EventLoader.kt +++ b/src/com/android/calendar/EventLoader.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,44 +13,37 @@ * 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.os.Handler; -import android.os.Process; -import android.provider.CalendarContract; -import android.provider.CalendarContract.EventDays; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; - -public class EventLoader { - - private Context mContext; - private Handler mHandler = new Handler(); - private AtomicInteger mSequenceNumber = new AtomicInteger(); - - private LinkedBlockingQueue mLoaderQueue; - private LoaderThread mLoaderThread; - private ContentResolver mResolver; - - private static interface LoadRequest { - public void processRequest(EventLoader eventLoader); - public void skipRequest(EventLoader eventLoader); +package com.android.calendar + +import android.content.ContentResolver +import android.content.Context +import android.database.Cursor +import android.os.Handler +import android.os.Process +import android.provider.CalendarContract +import android.provider.CalendarContract.EventDays +import android.util.Log +import java.util.ArrayList +import java.util.Arrays +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.atomic.AtomicInteger + +class EventLoader(context: Context) { + private val mContext: Context + private val mHandler: Handler = Handler() + private val mSequenceNumber: AtomicInteger = AtomicInteger() + private val mLoaderQueue: LinkedBlockingQueue + private var mLoaderThread: LoaderThread? = null + private val mResolver: ContentResolver + + private interface LoadRequest { + fun processRequest(eventLoader: EventLoader?) + fun skipRequest(eventLoader: EventLoader?) } - private static class ShutdownRequest implements LoadRequest { - public void processRequest(EventLoader eventLoader) { - } - - public void skipRequest(EventLoader eventLoader) { - } + private class ShutdownRequest : LoadRequest { + override fun processRequest(eventLoader: EventLoader?) {} + override fun skipRequest(eventLoader: EventLoader?) {} } /** @@ -59,202 +52,182 @@ public class EventLoader { * and filling in the eventDays array. * */ - private static class LoadEventDaysRequest implements LoadRequest { - public int startDay; - public int numDays; - public boolean[] eventDays; - public Runnable uiCallback; - - /** - * The projection used by the EventDays query. - */ - private static final String[] PROJECTION = { - CalendarContract.EventDays.STARTDAY, CalendarContract.EventDays.ENDDAY - }; - - public LoadEventDaysRequest(int startDay, int numDays, boolean[] eventDays, - final Runnable uiCallback) - { - this.startDay = startDay; - this.numDays = numDays; - this.eventDays = eventDays; - this.uiCallback = uiCallback; - } - + private class LoadEventDaysRequest(var startDay: Int, var numDays: Int, + var eventDays: BooleanArray, + uiCallback: Runnable) : LoadRequest { + var uiCallback: Runnable @Override - public void processRequest(EventLoader eventLoader) - { - final Handler handler = eventLoader.mHandler; - ContentResolver cr = eventLoader.mResolver; + override fun processRequest(eventLoader: EventLoader?) { + val handler: Handler? = eventLoader?.mHandler + val cr: ContentResolver? = eventLoader?.mResolver // Clear the event days - Arrays.fill(eventDays, false); + Arrays.fill(eventDays, false) //query which days have events - Cursor cursor = EventDays.query(cr, startDay, numDays, PROJECTION); + val cursor: Cursor = EventDays.query(cr, startDay, numDays, PROJECTION) try { - int startDayColumnIndex = cursor.getColumnIndexOrThrow(EventDays.STARTDAY); - int endDayColumnIndex = cursor.getColumnIndexOrThrow(EventDays.ENDDAY); + val startDayColumnIndex: Int = cursor.getColumnIndexOrThrow(EventDays.STARTDAY) + val endDayColumnIndex: Int = cursor.getColumnIndexOrThrow(EventDays.ENDDAY) //Set all the days with events to true while (cursor.moveToNext()) { - int firstDay = cursor.getInt(startDayColumnIndex); - int lastDay = cursor.getInt(endDayColumnIndex); + val firstDay: Int = cursor.getInt(startDayColumnIndex) + val lastDay: Int = cursor.getInt(endDayColumnIndex) //we want the entire range the event occurs, but only within the month - int firstIndex = Math.max(firstDay - startDay, 0); - int lastIndex = Math.min(lastDay - startDay, 30); - - for(int i = firstIndex; i <= lastIndex; i++) { - eventDays[i] = true; + val firstIndex: Int = Math.max(firstDay - startDay, 0) + val lastIndex: Int = Math.min(lastDay - startDay, 30) + for (i in firstIndex..lastIndex) { + eventDays[i] = true } } } finally { if (cursor != null) { - cursor.close(); + cursor.close() } } - handler.post(uiCallback); + handler?.post(uiCallback) } @Override - public void skipRequest(EventLoader eventLoader) { + override fun skipRequest(eventLoader: EventLoader?) { } - } - - private static class LoadEventsRequest implements LoadRequest { - public int id; - public int startDay; - public int numDays; - public ArrayList events; - public Runnable successCallback; - public Runnable cancelCallback; + companion object { + /** + * The projection used by the EventDays query. + */ + private val PROJECTION = arrayOf( + CalendarContract.EventDays.STARTDAY, CalendarContract.EventDays.ENDDAY + ) + } - public LoadEventsRequest(int id, int startDay, int numDays, ArrayList events, - final Runnable successCallback, final Runnable cancelCallback) { - this.id = id; - this.startDay = startDay; - this.numDays = numDays; - this.events = events; - this.successCallback = successCallback; - this.cancelCallback = cancelCallback; + init { + this.uiCallback = uiCallback } + } - public void processRequest(EventLoader eventLoader) { - Event.loadEvents(eventLoader.mContext, events, startDay, - numDays, id, eventLoader.mSequenceNumber); + private class LoadEventsRequest(var id: Int, var startDay: Int, var numDays: Int, + events: ArrayList, successCallback: Runnable, + cancelCallback: Runnable) : LoadRequest { + var events: ArrayList + var successCallback: Runnable + var cancelCallback: Runnable + @Override + override fun processRequest(eventLoader: EventLoader?) { + Event.loadEvents(eventLoader?.mContext, events, startDay, + numDays, id, eventLoader?.mSequenceNumber) // Check if we are still the most recent request. - if (id == eventLoader.mSequenceNumber.get()) { - eventLoader.mHandler.post(successCallback); + if (id == eventLoader?.mSequenceNumber?.get()) { + eventLoader?.mHandler?.post(successCallback) } else { - eventLoader.mHandler.post(cancelCallback); + eventLoader?.mHandler?.post(cancelCallback) } } - public void skipRequest(EventLoader eventLoader) { - eventLoader.mHandler.post(cancelCallback); + @Override + override fun skipRequest(eventLoader: EventLoader?) { + eventLoader?.mHandler?.post(cancelCallback) } - } - - private static class LoaderThread extends Thread { - LinkedBlockingQueue mQueue; - EventLoader mEventLoader; - public LoaderThread(LinkedBlockingQueue queue, EventLoader eventLoader) { - mQueue = queue; - mEventLoader = eventLoader; + init { + this.events = events + this.successCallback = successCallback + this.cancelCallback = cancelCallback } + } - public void shutdown() { + private class LoaderThread(queue: LinkedBlockingQueue, + eventLoader: EventLoader) : Thread() { + var mQueue: LinkedBlockingQueue + var mEventLoader: EventLoader + fun shutdown() { try { - mQueue.put(new ShutdownRequest()); - } catch (InterruptedException ex) { + mQueue.put(ShutdownRequest()) + } catch (ex: InterruptedException) { // The put() method fails with InterruptedException if the // queue is full. This should never happen because the queue // has no limit. - Log.e("Cal", "LoaderThread.shutdown() interrupted!"); + Log.e("Cal", "LoaderThread.shutdown() interrupted!") } } @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + override fun run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND) while (true) { try { // Wait for the next request - LoadRequest request = mQueue.take(); + var request: LoadRequest = mQueue.take() // If there are a bunch of requests already waiting, then // skip all but the most recent request. while (!mQueue.isEmpty()) { // Let the request know that it was skipped - request.skipRequest(mEventLoader); + request.skipRequest(mEventLoader) // Skip to the next request - request = mQueue.take(); + request = mQueue.take() } - - if (request instanceof ShutdownRequest) { - return; + if (request is ShutdownRequest) { + return } - request.processRequest(mEventLoader); - } catch (InterruptedException ex) { - Log.e("Cal", "background LoaderThread interrupted!"); + request.processRequest(mEventLoader) + } catch (ex: InterruptedException) { + Log.e("Cal", "background LoaderThread interrupted!") } } } - } - public EventLoader(Context context) { - mContext = context; - mLoaderQueue = new LinkedBlockingQueue(); - mResolver = context.getContentResolver(); + init { + mQueue = queue + mEventLoader = eventLoader + } } /** * Call this from the activity's onResume() */ - public void startBackgroundThread() { - mLoaderThread = new LoaderThread(mLoaderQueue, this); - mLoaderThread.start(); + fun startBackgroundThread() { + mLoaderThread = LoaderThread(mLoaderQueue, this) + mLoaderThread?.start() } /** * Call this from the activity's onPause() */ - public void stopBackgroundThread() { - mLoaderThread.shutdown(); + fun stopBackgroundThread() { + mLoaderThread!!.shutdown() } /** * Loads "numDays" days worth of events, starting at start, into events. - * Posts uiCallback to the {@link Handler} for this view, which will run in the UI thread. + * Posts uiCallback to the [Handler] for this view, which will run in the UI thread. * Reuses an existing background thread, if events were already being loaded in the background. * NOTE: events and uiCallback are not used if an existing background thread gets reused -- * the ones that were passed in on the call that results in the background thread getting * created are used, and the most recent call's worth of data is loaded into events and posted * via the uiCallback. */ - public void loadEventsInBackground(final int numDays, final ArrayList events, - int startDay, final Runnable successCallback, final Runnable cancelCallback) { + fun loadEventsInBackground(numDays: Int, events: ArrayList, + startDay: Int, successCallback: Runnable, cancelCallback: Runnable) { // Increment the sequence number for requests. We don't care if the // sequence numbers wrap around because we test for equality with the // latest one. - int id = mSequenceNumber.incrementAndGet(); + val id: Int = mSequenceNumber.incrementAndGet() // Send the load request to the background thread - LoadEventsRequest request = new LoadEventsRequest(id, startDay, numDays, - events, successCallback, cancelCallback); - + val request = LoadEventsRequest(id, startDay, numDays, + events, successCallback, cancelCallback) try { - mLoaderQueue.put(request); - } catch (InterruptedException ex) { + mLoaderQueue.put(request) + } catch (ex: InterruptedException) { // The put() method fails with InterruptedException if the // queue is full. This should never happen because the queue // has no limit. - Log.e("Cal", "loadEventsInBackground() interrupted!"); + Log.e("Cal", "loadEventsInBackground() interrupted!") } } @@ -268,19 +241,24 @@ public class EventLoader { * @param eventDay Whether or not an event exists on that day * @param uiCallback What to do when done (log data, redraw screen) */ - void loadEventDaysInBackground(int startDay, int numDays, boolean[] eventDays, - final Runnable uiCallback) - { + fun loadEventDaysInBackground(startDay: Int, numDays: Int, eventDays: BooleanArray, + uiCallback: Runnable) { // Send load request to the background thread - LoadEventDaysRequest request = new LoadEventDaysRequest(startDay, numDays, - eventDays, uiCallback); + val request = LoadEventDaysRequest(startDay, numDays, + eventDays, uiCallback) try { - mLoaderQueue.put(request); - } catch (InterruptedException ex) { + mLoaderQueue.put(request) + } catch (ex: InterruptedException) { // The put() method fails with InterruptedException if the // queue is full. This should never happen because the queue // has no limit. - Log.e("Cal", "loadEventDaysInBackground() interrupted!"); + Log.e("Cal", "loadEventDaysInBackground() interrupted!") } } -} + + init { + mContext = context + mLoaderQueue = LinkedBlockingQueue() + mResolver = context.getContentResolver() + } +} \ No newline at end of file -- cgit v1.2.3 From 3d72547d8524cc71a6deaed4754ffb49152f323d Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Mon, 21 Jun 2021 19:47:52 +0000 Subject: AOSP/Calendar - Copy of StickyHeaderListView.java The Java code in StickyHeaderListView.java has been copied into a corresponding .kt file. Test: manual - opening both .java and .kt files shows that they are identical. Change-Id: I2f7d252435677b2d6d7268257af2b49cbc6a7a14 --- src/com/android/calendar/StickyHeaderListView.kt | 395 +++++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 src/com/android/calendar/StickyHeaderListView.kt diff --git a/src/com/android/calendar/StickyHeaderListView.kt b/src/com/android/calendar/StickyHeaderListView.kt new file mode 100644 index 00000000..981e7af7 --- /dev/null +++ b/src/com/android/calendar/StickyHeaderListView.kt @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2011 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.Context; +import android.graphics.Color; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; +import android.widget.Adapter; +import android.widget.FrameLayout; +import android.widget.ListView; + +/** + * Implements a ListView class with a sticky header at the top. The header is + * per section and it is pinned to the top as long as its section is at the top + * of the view. If it is not, the header slides up or down (depending on the + * scroll movement) and the header of the current section slides to the top. + * Notes: + * 1. The class uses the first available child ListView as the working + * ListView. If no ListView child exists, the class will create a default one. + * 2. The ListView's adapter must be passed to this class using the 'setAdapter' + * method. The adapter must implement the HeaderIndexer interface. If no adapter + * is specified, the class will try to extract it from the ListView + * 3. The class registers itself as a listener to scroll events (OnScrollListener), if the + * ListView needs to receive scroll events, it must register its listener using + * this class' setOnScrollListener method. + * 4. Headers for the list view must be added before using the StickyHeaderListView + * 5. The implementation should register to listen to dataset changes. Right now this is not done + * since a change the dataset in a listview forces a call to OnScroll. The needed code is + * commented out. + */ +public class StickyHeaderListView extends FrameLayout implements OnScrollListener { + + private static final String TAG = "StickyHeaderListView"; + protected boolean mChildViewsCreated = false; + protected boolean mDoHeaderReset = false; + + protected Context mContext = null; + protected Adapter mAdapter = null; + protected HeaderIndexer mIndexer = null; + protected HeaderHeightListener mHeaderHeightListener = null; + protected View mStickyHeader = null; + protected View mNonessentialHeader = null; // A invisible header used when a section has no header + protected ListView mListView = null; + protected ListView.OnScrollListener mListener = null; + + private int mSeparatorWidth; + private View mSeparatorView; + private int mLastStickyHeaderHeight = 0; + + // This code is needed only if dataset changes do not force a call to OnScroll + // protected DataSetObserver mListDataObserver = null; + + + protected int mCurrentSectionPos = -1; // Position of section that has its header on the + // top of the view + protected int mNextSectionPosition = -1; // Position of next section's header + protected int mListViewHeadersCount = 0; + + /** + * Interface that must be implemented by the ListView adapter to provide headers locations + * and number of items under each header. + * + */ + public interface HeaderIndexer { + /** + * Calculates the position of the header of a specific item in the adapter's data set. + * For example: Assuming you have a list with albums and songs names: + * Album A, song 1, song 2, ...., song 10, Album B, song 1, ..., song 7. A call to + * this method with the position of song 5 in Album B, should return the position + * of Album B. + * @param position - Position of the item in the ListView dataset + * @return Position of header. -1 if the is no header + */ + + int getHeaderPositionFromItemPosition(int position); + + /** + * Calculates the number of items in the section defined by the header (not including + * the header). + * For example: A list with albums and songs, the method should return + * the number of songs names (without the album name). + * + * @param headerPosition - the value returned by 'getHeaderPositionFromItemPosition' + * @return Number of items. -1 on error. + */ + int getHeaderItemsNumber(int headerPosition); + } + + /*** + * + * Interface that is used to update the sticky header's height + * + */ + public interface HeaderHeightListener { + + /*** + * Updated a change in the sticky header's size + * + * @param height - new height of sticky header + */ + void OnHeaderHeightChanged(int height); + } + + /** + * Sets the adapter to be used by the class to get views of headers + * + * @param adapter - The adapter. + */ + + public void setAdapter(Adapter adapter) { + + // This code is needed only if dataset changes do not force a call to + // OnScroll + // if (mAdapter != null && mListDataObserver != null) { + // mAdapter.unregisterDataSetObserver(mListDataObserver); + // } + + if (adapter != null) { + mAdapter = adapter; + // This code is needed only if dataset changes do not force a call + // to OnScroll + // mAdapter.registerDataSetObserver(mListDataObserver); + } + } + + /** + * Sets the indexer object (that implements the HeaderIndexer interface). + * + * @param indexer - The indexer. + */ + + public void setIndexer(HeaderIndexer indexer) { + mIndexer = indexer; + } + + /** + * Sets the list view that is displayed + * @param lv - The list view. + */ + + public void setListView(ListView lv) { + mListView = lv; + mListView.setOnScrollListener(this); + mListViewHeadersCount = mListView.getHeaderViewsCount(); + } + + /** + * Sets an external OnScroll listener. Since the StickyHeaderListView sets + * itself as the scroll events listener of the listview, this method allows + * the user to register another listener that will be called after this + * class listener is called. + * + * @param listener - The external listener. + */ + public void setOnScrollListener(ListView.OnScrollListener listener) { + mListener = listener; + } + + public void setHeaderHeightListener(HeaderHeightListener listener) { + mHeaderHeightListener = listener; + } + + // This code is needed only if dataset changes do not force a call to OnScroll + // protected void createDataListener() { + // mListDataObserver = new DataSetObserver() { + // @Override + // public void onChanged() { + // onDataChanged(); + // } + // }; + // } + + /** + * Constructor + * + * @param context - application context. + * @param attrs - layout attributes. + */ + public StickyHeaderListView(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + // This code is needed only if dataset changes do not force a call to OnScroll + // createDataListener(); + } + + /** + * Scroll status changes listener + * + * @param view - the scrolled view + * @param scrollState - new scroll state. + */ + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + if (mListener != null) { + mListener.onScrollStateChanged(view, scrollState); + } + } + + /** + * Scroll events listener + * + * @param view - the scrolled view + * @param firstVisibleItem - the index (in the list's adapter) of the top + * visible item. + * @param visibleItemCount - the number of visible items in the list + * @param totalItemCount - the total number items in the list + */ + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, + int totalItemCount) { + + updateStickyHeader(firstVisibleItem); + + if (mListener != null) { + mListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); + } + } + + /** + * Sets a separator below the sticky header, which will be visible while the sticky header + * is not scrolling up. + * @param color - color of separator + * @param width - width in pixels of separator + */ + public void setHeaderSeparator(int color, int width) { + mSeparatorView = new View(mContext); + ViewGroup.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, + width, Gravity.TOP); + mSeparatorView.setLayoutParams(params); + mSeparatorView.setBackgroundColor(color); + mSeparatorWidth = width; + this.addView(mSeparatorView); + } + + protected void updateStickyHeader(int firstVisibleItem) { + + // Try to make sure we have an adapter to work with (may not succeed). + if (mAdapter == null && mListView != null) { + setAdapter(mListView.getAdapter()); + } + + firstVisibleItem -= mListViewHeadersCount; + if (mAdapter != null && mIndexer != null && mDoHeaderReset) { + + // Get the section header position + int sectionSize = 0; + int sectionPos = mIndexer.getHeaderPositionFromItemPosition(firstVisibleItem); + + // New section - set it in the header view + boolean newView = false; + if (sectionPos != mCurrentSectionPos) { + + // No header for current position , use the nonessential invisible one, hide the separator + if (sectionPos == -1) { + sectionSize = 0; + this.removeView(mStickyHeader); + mStickyHeader = mNonessentialHeader; + if (mSeparatorView != null) { + mSeparatorView.setVisibility(View.GONE); + } + newView = true; + } else { + // Create a copy of the header view to show on top + sectionSize = mIndexer.getHeaderItemsNumber(sectionPos); + View v = mAdapter.getView(sectionPos + mListViewHeadersCount, null, mListView); + v.measure(MeasureSpec.makeMeasureSpec(mListView.getWidth(), + MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mListView.getHeight(), + MeasureSpec.AT_MOST)); + this.removeView(mStickyHeader); + mStickyHeader = v; + newView = true; + } + mCurrentSectionPos = sectionPos; + mNextSectionPosition = sectionSize + sectionPos + 1; + } + + + // Do transitions + // If position of bottom of last item in a section is smaller than the height of the + // sticky header - shift drawable of header. + if (mStickyHeader != null) { + int sectionLastItemPosition = mNextSectionPosition - firstVisibleItem - 1; + int stickyHeaderHeight = mStickyHeader.getHeight(); + if (stickyHeaderHeight == 0) { + stickyHeaderHeight = mStickyHeader.getMeasuredHeight(); + } + + // Update new header height + if (mHeaderHeightListener != null && + mLastStickyHeaderHeight != stickyHeaderHeight) { + mLastStickyHeaderHeight = stickyHeaderHeight; + mHeaderHeightListener.OnHeaderHeightChanged(stickyHeaderHeight); + } + + View SectionLastView = mListView.getChildAt(sectionLastItemPosition); + if (SectionLastView != null && SectionLastView.getBottom() <= stickyHeaderHeight) { + int lastViewBottom = SectionLastView.getBottom(); + mStickyHeader.setTranslationY(lastViewBottom - stickyHeaderHeight); + if (mSeparatorView != null) { + mSeparatorView.setVisibility(View.GONE); + } + } else if (stickyHeaderHeight != 0) { + mStickyHeader.setTranslationY(0); + if (mSeparatorView != null && !mStickyHeader.equals(mNonessentialHeader)) { + mSeparatorView.setVisibility(View.VISIBLE); + } + } + if (newView) { + mStickyHeader.setVisibility(View.INVISIBLE); + this.addView(mStickyHeader); + if (mSeparatorView != null && !mStickyHeader.equals(mNonessentialHeader)){ + FrameLayout.LayoutParams params = + new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, + mSeparatorWidth); + params.setMargins(0, mStickyHeader.getMeasuredHeight(), 0, 0); + mSeparatorView.setLayoutParams(params); + mSeparatorView.setVisibility(View.VISIBLE); + } + mStickyHeader.setVisibility(View.VISIBLE); + } + } + } + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + if (!mChildViewsCreated) { + setChildViews(); + } + mDoHeaderReset = true; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (!mChildViewsCreated) { + setChildViews(); + } + mDoHeaderReset = true; + } + + + // Resets the sticky header when the adapter data set was changed + // This code is needed only if dataset changes do not force a call to OnScroll + // protected void onDataChanged() { + // Should do a call to updateStickyHeader if needed + // } + + private void setChildViews() { + + // Find a child ListView (if any) + int iChildNum = getChildCount(); + for (int i = 0; i < iChildNum; i++) { + Object v = getChildAt(i); + if (v instanceof ListView) { + setListView((ListView) v); + } + } + + // No child ListView - add one + if (mListView == null) { + setListView(new ListView(mContext)); + } + + // Create a nonessential view , it will be used in case a section has no header + mNonessentialHeader = new View (mContext); + ViewGroup.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, + 1, Gravity.TOP); + mNonessentialHeader.setLayoutParams(params); + mNonessentialHeader.setBackgroundColor(Color.TRANSPARENT); + + mChildViewsCreated = true; + } + +} -- cgit v1.2.3 From fc65c97b0cb18d611244e6c7f280021c36b64841 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Tue, 22 Jun 2021 17:39:44 +0000 Subject: AOSP/Calendar - Add Kotlin copy of DayFragment.java Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: Ic7d37c27748cb708fd25099fc2b20ce6af3719d4 --- src/com/android/calendar/DayFragment.kt | 256 ++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 src/com/android/calendar/DayFragment.kt diff --git a/src/com/android/calendar/DayFragment.kt b/src/com/android/calendar/DayFragment.kt new file mode 100644 index 00000000..a9fb39ed --- /dev/null +++ b/src/com/android/calendar/DayFragment.kt @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2010 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 com.android.calendar.CalendarController.EventInfo; +import com.android.calendar.CalendarController.EventType; + +import android.app.Fragment; +import android.content.Context; +import android.os.Bundle; +import android.text.format.Time; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.ProgressBar; +import android.widget.ViewSwitcher; +import android.widget.ViewSwitcher.ViewFactory; + +/** + * This is the base class for Day and Week Activities. + */ +public class DayFragment extends Fragment implements CalendarController.EventHandler, ViewFactory { + /** + * The view id used for all the views we create. It's OK to have all child + * views have the same ID. This ID is used to pick which view receives + * focus when a view hierarchy is saved / restore + */ + private static final int VIEW_ID = 1; + + protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time"; + + protected ProgressBar mProgressBar; + protected ViewSwitcher mViewSwitcher; + protected Animation mInAnimationForward; + protected Animation mOutAnimationForward; + protected Animation mInAnimationBackward; + protected Animation mOutAnimationBackward; + EventLoader mEventLoader; + + Time mSelectedDay = new Time(); + + private final Runnable mTZUpdater = new Runnable() { + @Override + public void run() { + if (!DayFragment.this.isAdded()) { + return; + } + String tz = Utils.getTimeZone(getActivity(), mTZUpdater); + mSelectedDay.timezone = tz; + mSelectedDay.normalize(true); + } + }; + + private int mNumDays; + + public DayFragment() { + mSelectedDay.setToNow(); + } + + public DayFragment(long timeMillis, int numOfDays) { + mNumDays = numOfDays; + if (timeMillis == 0) { + mSelectedDay.setToNow(); + } else { + mSelectedDay.set(timeMillis); + } + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + Context context = getActivity(); + + mInAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_in); + mOutAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_out); + mInAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_in); + mOutAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_out); + + mEventLoader = new EventLoader(context); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.day_activity, null); + + mViewSwitcher = (ViewSwitcher) v.findViewById(R.id.switcher); + mViewSwitcher.setFactory(this); + mViewSwitcher.getCurrentView().requestFocus(); + ((DayView) mViewSwitcher.getCurrentView()).updateTitle(); + + return v; + } + + public View makeView() { + mTZUpdater.run(); + DayView view = new DayView(getActivity(), CalendarController + .getInstance(getActivity()), mViewSwitcher, mEventLoader, mNumDays); + view.setId(VIEW_ID); + view.setLayoutParams(new ViewSwitcher.LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + view.setSelected(mSelectedDay, false, false); + return view; + } + + @Override + public void onResume() { + super.onResume(); + mEventLoader.startBackgroundThread(); + mTZUpdater.run(); + eventsChanged(); + DayView view = (DayView) mViewSwitcher.getCurrentView(); + view.handleOnResume(); + view.restartCurrentTimeUpdates(); + + view = (DayView) mViewSwitcher.getNextView(); + view.handleOnResume(); + view.restartCurrentTimeUpdates(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + } + + @Override + public void onPause() { + super.onPause(); + DayView view = (DayView) mViewSwitcher.getCurrentView(); + view.cleanup(); + view = (DayView) mViewSwitcher.getNextView(); + view.cleanup(); + mEventLoader.stopBackgroundThread(); + + // Stop events cross-fade animation + view.stopEventsAnimation(); + ((DayView) mViewSwitcher.getNextView()).stopEventsAnimation(); + } + + void startProgressSpinner() { + // start the progress spinner + mProgressBar.setVisibility(View.VISIBLE); + } + + void stopProgressSpinner() { + // stop the progress spinner + mProgressBar.setVisibility(View.GONE); + } + + private void goTo(Time goToTime, boolean ignoreTime, boolean animateToday) { + if (mViewSwitcher == null) { + // The view hasn't been set yet. Just save the time and use it later. + mSelectedDay.set(goToTime); + return; + } + + DayView currentView = (DayView) mViewSwitcher.getCurrentView(); + + // How does goTo time compared to what's already displaying? + int diff = currentView.compareToVisibleTimeRange(goToTime); + + if (diff == 0) { + // In visible range. No need to switch view + currentView.setSelected(goToTime, ignoreTime, animateToday); + } else { + // Figure out which way to animate + if (diff > 0) { + mViewSwitcher.setInAnimation(mInAnimationForward); + mViewSwitcher.setOutAnimation(mOutAnimationForward); + } else { + mViewSwitcher.setInAnimation(mInAnimationBackward); + mViewSwitcher.setOutAnimation(mOutAnimationBackward); + } + + DayView next = (DayView) mViewSwitcher.getNextView(); + if (ignoreTime) { + next.setFirstVisibleHour(currentView.getFirstVisibleHour()); + } + + next.setSelected(goToTime, ignoreTime, animateToday); + next.reloadEvents(); + mViewSwitcher.showNext(); + next.requestFocus(); + next.updateTitle(); + next.restartCurrentTimeUpdates(); + } + } + + /** + * Returns the selected time in milliseconds. The milliseconds are measured + * in UTC milliseconds from the epoch and uniquely specifies any selectable + * time. + * + * @return the selected time in milliseconds + */ + public long getSelectedTimeInMillis() { + if (mViewSwitcher == null) { + return -1; + } + DayView view = (DayView) mViewSwitcher.getCurrentView(); + if (view == null) { + return -1; + } + return view.getSelectedTimeInMillis(); + } + + public void eventsChanged() { + if (mViewSwitcher == null) { + return; + } + DayView view = (DayView) mViewSwitcher.getCurrentView(); + view.clearCachedEvents(); + view.reloadEvents(); + + view = (DayView) mViewSwitcher.getNextView(); + view.clearCachedEvents(); + } + + public DayView getNextView() { + return (DayView) mViewSwitcher.getNextView(); + } + + public long getSupportedEventTypes() { + return EventType.GO_TO | EventType.EVENTS_CHANGED; + } + + public void handleEvent(EventInfo msg) { + if (msg.eventType == EventType.GO_TO) { +// TODO support a range of time +// TODO support event_id +// TODO support select message + goTo(msg.selectedTime, (msg.extraLong & CalendarController.EXTRA_GOTO_DATE) != 0, + (msg.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0); + } else if (msg.eventType == EventType.EVENTS_CHANGED) { + eventsChanged(); + } + } +} -- cgit v1.2.3 From c2556b08f3706ed31791116776fb7ba25135359d Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Tue, 22 Jun 2021 12:56:01 +0000 Subject: AOSP Calendar - StickyHeaderListView fully converted with bp file Using Android Studio, StickyHeaderListView.java was fully converted to Kotlin. The converter failed to add override modifiers to those methods that properly overrode a method from a superclass. Another recurring issue that isn't new is the converter's failure to make object references nullable since any object in Java may be null. For the most part nullability had to manually dealt with. Another unique issue is that ListView.OnScrollListener seemed to be an unresolved reference according to the Kotlin compiler. Since we import android.widget.AbsListView.OnScrollListener, this issue was solved by using AbsListView.OnScrollListener to capture the same behavior. In fact, according to both Java and Kotlin docs, OnScrollListener is an interface that should only be called upon AbsListView. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.155 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 5m 0s Total aggregated tests run time: 5m 0s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 1.25 tests/sec [376 tests / 300519 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 6013 ms || clean = 1936 ms Total preparation time: 6s || Total tear down time: 1s ======================================================= =============== Summary =============== Total Run time: 6m 26s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: I76262444fb08e094876b46d06555f43dafcdc48f --- Android.bp | 1 + src/com/android/calendar/StickyHeaderListView.kt | 377 +++++++++++------------ 2 files changed, 185 insertions(+), 193 deletions(-) diff --git a/Android.bp b/Android.bp index f8c7e210..64d043a6 100644 --- a/Android.bp +++ b/Android.bp @@ -30,6 +30,7 @@ exclude_srcsd = [ "src/**/calendar/CalendarController.java", "src/**/calendar/DayOfMonthDrawable.java", "src/**/calendar/EventInfoActivity.java", + "src/**/calendar/StickyHeaderListView.java", ] exclude_srcsm = [ diff --git a/src/com/android/calendar/StickyHeaderListView.kt b/src/com/android/calendar/StickyHeaderListView.kt index 981e7af7..37733b7b 100644 --- a/src/com/android/calendar/StickyHeaderListView.kt +++ b/src/com/android/calendar/StickyHeaderListView.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,20 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar; - -import android.content.Context; -import android.graphics.Color; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; -import android.widget.Adapter; -import android.widget.FrameLayout; -import android.widget.ListView; +package com.android.calendar + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import android.view.Gravity +import android.view.View +import android.view.ViewGroup +import android.widget.AbsListView +import android.widget.AbsListView.OnScrollListener +import android.widget.Adapter +import android.widget.FrameLayout +import android.widget.ListView /** * Implements a ListView class with a sticky header at the top. The header is @@ -35,52 +34,49 @@ import android.widget.ListView; * scroll movement) and the header of the current section slides to the top. * Notes: * 1. The class uses the first available child ListView as the working - * ListView. If no ListView child exists, the class will create a default one. + * ListView. If no ListView child exists, the class will create a default one. * 2. The ListView's adapter must be passed to this class using the 'setAdapter' - * method. The adapter must implement the HeaderIndexer interface. If no adapter - * is specified, the class will try to extract it from the ListView + * method. The adapter must implement the HeaderIndexer interface. If no adapter + * is specified, the class will try to extract it from the ListView * 3. The class registers itself as a listener to scroll events (OnScrollListener), if the - * ListView needs to receive scroll events, it must register its listener using - * this class' setOnScrollListener method. + * ListView needs to receive scroll events, it must register its listener using + * this class' setOnScrollListener method. * 4. Headers for the list view must be added before using the StickyHeaderListView * 5. The implementation should register to listen to dataset changes. Right now this is not done - * since a change the dataset in a listview forces a call to OnScroll. The needed code is - * commented out. + * since a change the dataset in a listview forces a call to OnScroll. The needed code is + * commented out. */ -public class StickyHeaderListView extends FrameLayout implements OnScrollListener { - - private static final String TAG = "StickyHeaderListView"; - protected boolean mChildViewsCreated = false; - protected boolean mDoHeaderReset = false; - - protected Context mContext = null; - protected Adapter mAdapter = null; - protected HeaderIndexer mIndexer = null; - protected HeaderHeightListener mHeaderHeightListener = null; - protected View mStickyHeader = null; - protected View mNonessentialHeader = null; // A invisible header used when a section has no header - protected ListView mListView = null; - protected ListView.OnScrollListener mListener = null; - - private int mSeparatorWidth; - private View mSeparatorView; - private int mLastStickyHeaderHeight = 0; +class StickyHeaderListView(context: Context, attrs: AttributeSet?) : + FrameLayout(context, attrs), OnScrollListener { + protected var mChildViewsCreated = false + protected var mDoHeaderReset = false + protected var mContext: Context? = null + protected var mAdapter: Adapter? = null + protected var mIndexer: HeaderIndexer? = null + protected var mHeaderHeightListener: HeaderHeightListener? = null + protected var mStickyHeader: View? = null + // A invisible header used when a section has no header + protected var mNonessentialHeader: View? = null + protected var mListView: ListView? = null + protected var mListener: AbsListView.OnScrollListener? = null + private var mSeparatorWidth = 0 + private var mSeparatorView: View? = null + private var mLastStickyHeaderHeight = 0 // This code is needed only if dataset changes do not force a call to OnScroll // protected DataSetObserver mListDataObserver = null; + protected var mCurrentSectionPos = -1 // Position of section that has its header on the - - protected int mCurrentSectionPos = -1; // Position of section that has its header on the - // top of the view - protected int mNextSectionPosition = -1; // Position of next section's header - protected int mListViewHeadersCount = 0; + // top of the view + protected var mNextSectionPosition = -1 // Position of next section's header + protected var mListViewHeadersCount = 0 /** * Interface that must be implemented by the ListView adapter to provide headers locations * and number of items under each header. * */ - public interface HeaderIndexer { + interface HeaderIndexer { /** * Calculates the position of the header of a specific item in the adapter's data set. * For example: Assuming you have a list with albums and songs names: @@ -90,8 +86,7 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene * @param position - Position of the item in the ListView dataset * @return Position of header. -1 if the is no header */ - - int getHeaderPositionFromItemPosition(int position); + fun getHeaderPositionFromItemPosition(position: Int): Int /** * Calculates the number of items in the section defined by the header (not including @@ -102,40 +97,36 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene * @param headerPosition - the value returned by 'getHeaderPositionFromItemPosition' * @return Number of items. -1 on error. */ - int getHeaderItemsNumber(int headerPosition); + fun getHeaderItemsNumber(headerPosition: Int): Int } /*** - * - * Interface that is used to update the sticky header's height - * - */ - public interface HeaderHeightListener { - - /*** - * Updated a change in the sticky header's size - * - * @param height - new height of sticky header - */ - void OnHeaderHeightChanged(int height); - } + * + * Interface that is used to update the sticky header's height + * + */ + interface HeaderHeightListener { + /*** + * Updated a change in the sticky header's size + * + * @param height - new height of sticky header + */ + fun OnHeaderHeightChanged(height: Int) + } /** * Sets the adapter to be used by the class to get views of headers * * @param adapter - The adapter. */ - - public void setAdapter(Adapter adapter) { - + fun setAdapter(adapter: Adapter?) { // This code is needed only if dataset changes do not force a call to // OnScroll // if (mAdapter != null && mListDataObserver != null) { // mAdapter.unregisterDataSetObserver(mListDataObserver); // } - if (adapter != null) { - mAdapter = adapter; + mAdapter = adapter // This code is needed only if dataset changes do not force a call // to OnScroll // mAdapter.registerDataSetObserver(mListDataObserver); @@ -147,20 +138,18 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene * * @param indexer - The indexer. */ - - public void setIndexer(HeaderIndexer indexer) { - mIndexer = indexer; + fun setIndexer(indexer: HeaderIndexer?) { + mIndexer = indexer } /** * Sets the list view that is displayed * @param lv - The list view. */ - - public void setListView(ListView lv) { - mListView = lv; - mListView.setOnScrollListener(this); - mListViewHeadersCount = mListView.getHeaderViewsCount(); + fun setListView(lv: ListView?) { + mListView = lv + mListView?.setOnScrollListener(this) + mListViewHeadersCount = mListView?.getHeaderViewsCount() as Int } /** @@ -171,37 +160,14 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene * * @param listener - The external listener. */ - public void setOnScrollListener(ListView.OnScrollListener listener) { - mListener = listener; + fun setOnScrollListener(listener: AbsListView.OnScrollListener?) { + mListener = listener } - public void setHeaderHeightListener(HeaderHeightListener listener) { - mHeaderHeightListener = listener; + fun setHeaderHeightListener(listener: HeaderHeightListener?) { + mHeaderHeightListener = listener } - // This code is needed only if dataset changes do not force a call to OnScroll - // protected void createDataListener() { - // mListDataObserver = new DataSetObserver() { - // @Override - // public void onChanged() { - // onDataChanged(); - // } - // }; - // } - - /** - * Constructor - * - * @param context - application context. - * @param attrs - layout attributes. - */ - public StickyHeaderListView(Context context, AttributeSet attrs) { - super(context, attrs); - mContext = context; - // This code is needed only if dataset changes do not force a call to OnScroll - // createDataListener(); - } - /** * Scroll status changes listener * @@ -209,9 +175,9 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene * @param scrollState - new scroll state. */ @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { + override fun onScrollStateChanged(view: AbsListView?, scrollState: Int) { if (mListener != null) { - mListener.onScrollStateChanged(view, scrollState); + mListener?.onScrollStateChanged(view, scrollState) } } @@ -220,18 +186,20 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene * * @param view - the scrolled view * @param firstVisibleItem - the index (in the list's adapter) of the top - * visible item. + * visible item. * @param visibleItemCount - the number of visible items in the list * @param totalItemCount - the total number items in the list */ @Override - public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, - int totalItemCount) { - - updateStickyHeader(firstVisibleItem); - + override fun onScroll( + view: AbsListView?, + firstVisibleItem: Int, + visibleItemCount: Int, + totalItemCount: Int + ) { + updateStickyHeader(firstVisibleItem) if (mListener != null) { - mListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); + mListener?.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount) } } @@ -241,155 +209,178 @@ public class StickyHeaderListView extends FrameLayout implements OnScrollListene * @param color - color of separator * @param width - width in pixels of separator */ - public void setHeaderSeparator(int color, int width) { - mSeparatorView = new View(mContext); - ViewGroup.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, - width, Gravity.TOP); - mSeparatorView.setLayoutParams(params); - mSeparatorView.setBackgroundColor(color); - mSeparatorWidth = width; - this.addView(mSeparatorView); + fun setHeaderSeparator(color: Int, width: Int) { + mSeparatorView = View(mContext) + val params: ViewGroup.LayoutParams = LayoutParams( + LayoutParams.MATCH_PARENT, + width, Gravity.TOP + ) + mSeparatorView?.setLayoutParams(params) + mSeparatorView?.setBackgroundColor(color) + mSeparatorWidth = width + this.addView(mSeparatorView) } - protected void updateStickyHeader(int firstVisibleItem) { - + protected fun updateStickyHeader(firstVisibleItemInput: Int) { // Try to make sure we have an adapter to work with (may not succeed). + var firstVisibleItem = firstVisibleItemInput if (mAdapter == null && mListView != null) { - setAdapter(mListView.getAdapter()); + setAdapter(mListView?.getAdapter()) } - - firstVisibleItem -= mListViewHeadersCount; + firstVisibleItem -= mListViewHeadersCount if (mAdapter != null && mIndexer != null && mDoHeaderReset) { // Get the section header position - int sectionSize = 0; - int sectionPos = mIndexer.getHeaderPositionFromItemPosition(firstVisibleItem); + var sectionSize = 0 + val sectionPos = mIndexer!!.getHeaderPositionFromItemPosition(firstVisibleItem) // New section - set it in the header view - boolean newView = false; + var newView = false if (sectionPos != mCurrentSectionPos) { - // No header for current position , use the nonessential invisible one, hide the separator + // No header for current position , use the nonessential invisible one, + // hide the separator if (sectionPos == -1) { - sectionSize = 0; - this.removeView(mStickyHeader); - mStickyHeader = mNonessentialHeader; + sectionSize = 0 + this.removeView(mStickyHeader) + mStickyHeader = mNonessentialHeader if (mSeparatorView != null) { - mSeparatorView.setVisibility(View.GONE); + mSeparatorView?.setVisibility(View.GONE) } - newView = true; + newView = true } else { // Create a copy of the header view to show on top - sectionSize = mIndexer.getHeaderItemsNumber(sectionPos); - View v = mAdapter.getView(sectionPos + mListViewHeadersCount, null, mListView); - v.measure(MeasureSpec.makeMeasureSpec(mListView.getWidth(), - MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mListView.getHeight(), - MeasureSpec.AT_MOST)); - this.removeView(mStickyHeader); - mStickyHeader = v; - newView = true; + sectionSize = mIndexer!!.getHeaderItemsNumber(sectionPos) + val v: View? = + mAdapter?.getView(sectionPos + mListViewHeadersCount, null, mListView) + v?.measure( + MeasureSpec.makeMeasureSpec( + mListView?.getWidth() as Int, + MeasureSpec.EXACTLY + ), MeasureSpec.makeMeasureSpec( + mListView?.getHeight() as Int, + MeasureSpec.AT_MOST + ) + ) + this.removeView(mStickyHeader) + mStickyHeader = v + newView = true } - mCurrentSectionPos = sectionPos; - mNextSectionPosition = sectionSize + sectionPos + 1; + mCurrentSectionPos = sectionPos + mNextSectionPosition = sectionSize + sectionPos + 1 } - // Do transitions // If position of bottom of last item in a section is smaller than the height of the // sticky header - shift drawable of header. if (mStickyHeader != null) { - int sectionLastItemPosition = mNextSectionPosition - firstVisibleItem - 1; - int stickyHeaderHeight = mStickyHeader.getHeight(); + val sectionLastItemPosition = mNextSectionPosition - firstVisibleItem - 1 + var stickyHeaderHeight: Int = mStickyHeader?.getHeight() as Int if (stickyHeaderHeight == 0) { - stickyHeaderHeight = mStickyHeader.getMeasuredHeight(); + stickyHeaderHeight = mStickyHeader?.getMeasuredHeight() as Int } // Update new header height if (mHeaderHeightListener != null && - mLastStickyHeaderHeight != stickyHeaderHeight) { - mLastStickyHeaderHeight = stickyHeaderHeight; - mHeaderHeightListener.OnHeaderHeightChanged(stickyHeaderHeight); + mLastStickyHeaderHeight != stickyHeaderHeight + ) { + mLastStickyHeaderHeight = stickyHeaderHeight + mHeaderHeightListener!!.OnHeaderHeightChanged(stickyHeaderHeight) } - - View SectionLastView = mListView.getChildAt(sectionLastItemPosition); + val SectionLastView: View? = mListView?.getChildAt(sectionLastItemPosition) if (SectionLastView != null && SectionLastView.getBottom() <= stickyHeaderHeight) { - int lastViewBottom = SectionLastView.getBottom(); - mStickyHeader.setTranslationY(lastViewBottom - stickyHeaderHeight); + val lastViewBottom: Int = SectionLastView.getBottom() + mStickyHeader?.setTranslationY(lastViewBottom.toFloat() - + stickyHeaderHeight.toFloat()) if (mSeparatorView != null) { - mSeparatorView.setVisibility(View.GONE); + mSeparatorView?.setVisibility(View.GONE) } } else if (stickyHeaderHeight != 0) { - mStickyHeader.setTranslationY(0); - if (mSeparatorView != null && !mStickyHeader.equals(mNonessentialHeader)) { - mSeparatorView.setVisibility(View.VISIBLE); + mStickyHeader?.setTranslationY(0f) + if (mSeparatorView != null && + mStickyHeader?.equals(mNonessentialHeader) == false) { + mSeparatorView?.setVisibility(View.VISIBLE) } } if (newView) { - mStickyHeader.setVisibility(View.INVISIBLE); - this.addView(mStickyHeader); - if (mSeparatorView != null && !mStickyHeader.equals(mNonessentialHeader)){ - FrameLayout.LayoutParams params = - new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, - mSeparatorWidth); - params.setMargins(0, mStickyHeader.getMeasuredHeight(), 0, 0); - mSeparatorView.setLayoutParams(params); - mSeparatorView.setVisibility(View.VISIBLE); + mStickyHeader?.setVisibility(View.INVISIBLE) + this.addView(mStickyHeader) + if (mSeparatorView != null && + mStickyHeader?.equals(mNonessentialHeader) == false) { + val params: FrameLayout.LayoutParams = LayoutParams( + LayoutParams.MATCH_PARENT, + mSeparatorWidth + ) + params.setMargins(0, mStickyHeader?.getMeasuredHeight() as Int, 0, 0) + mSeparatorView?.setLayoutParams(params) + mSeparatorView?.setVisibility(View.VISIBLE) } - mStickyHeader.setVisibility(View.VISIBLE); + mStickyHeader?.setVisibility(View.VISIBLE) } } } } @Override - protected void onFinishInflate() { - super.onFinishInflate(); + protected override fun onFinishInflate() { + super.onFinishInflate() if (!mChildViewsCreated) { - setChildViews(); + setChildViews() } - mDoHeaderReset = true; + mDoHeaderReset = true } @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); + protected override fun onAttachedToWindow() { + super.onAttachedToWindow() if (!mChildViewsCreated) { - setChildViews(); + setChildViews() } - mDoHeaderReset = true; + mDoHeaderReset = true } - // Resets the sticky header when the adapter data set was changed // This code is needed only if dataset changes do not force a call to OnScroll // protected void onDataChanged() { // Should do a call to updateStickyHeader if needed // } - - private void setChildViews() { - + private fun setChildViews() { // Find a child ListView (if any) - int iChildNum = getChildCount(); - for (int i = 0; i < iChildNum; i++) { - Object v = getChildAt(i); - if (v instanceof ListView) { - setListView((ListView) v); + val iChildNum: Int = getChildCount() + for (i in 0 until iChildNum) { + val v: Object = getChildAt(i) as Object + if (v is ListView) { + setListView(v as ListView) } } // No child ListView - add one if (mListView == null) { - setListView(new ListView(mContext)); + setListView(ListView(mContext)) } // Create a nonessential view , it will be used in case a section has no header - mNonessentialHeader = new View (mContext); - ViewGroup.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, - 1, Gravity.TOP); - mNonessentialHeader.setLayoutParams(params); - mNonessentialHeader.setBackgroundColor(Color.TRANSPARENT); + mNonessentialHeader = View(mContext) + val params: ViewGroup.LayoutParams = LayoutParams( + LayoutParams.MATCH_PARENT, + 1, Gravity.TOP + ) + mNonessentialHeader?.setLayoutParams(params) + mNonessentialHeader?.setBackgroundColor(Color.TRANSPARENT) + mChildViewsCreated = true + } - mChildViewsCreated = true; + companion object { + private const val TAG = "StickyHeaderListView" } -} + /** + * Constructor + * + * @param context - application context. + * @param attrs - layout attributes. + */ + init { + mContext = context + } +} \ No newline at end of file -- cgit v1.2.3 From 1fdffcdc6e348905d0cdb998ff6d0854911ed2ff Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Tue, 22 Jun 2021 20:14:59 +0000 Subject: AOSP/Calendar - Add Kotlin code for DayFragment.kt. Also uploaded corresponding Android.bp file No major changes in this file. A few functions required override modifiers and a few variable declarations to include nullability references. Test: manual (ran build and unit tests) and CTS testing suite $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.136 OK (22 tests) CTS TESTING: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ============================================ ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 8m 33s Total aggregated tests run time: 8m 33s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.73 tests/sec [376 tests / 513938 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 9371 ms || clean = 3271 ms Total preparation time: 9s || Total tear down time: 3s ======================================================= =============== Summary =============== Total Run time: 14m 52s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: Ifc3a6b8e9a2024cf0169491480db3de9c787b96a --- Android.bp | 1 + src/com/android/calendar/DayFragment.kt | 338 +++++++++++++++----------------- 2 files changed, 157 insertions(+), 182 deletions(-) diff --git a/Android.bp b/Android.bp index f8c7e210..cc2be4b1 100644 --- a/Android.bp +++ b/Android.bp @@ -37,6 +37,7 @@ exclude_srcsm = [ "src/**/calendar/CalendarApplication.java", "src/**/calendar/CalendarBackupAgent.java", "src/**/calendar/CalendarUtils.java", + "src/**/calendar/DayFragment.java", "src/**/calendar/EventGeometry.java", "src/**/calendar/EventLoader.java", "src/**/calendar/GeneralPreferences.java", diff --git a/src/com/android/calendar/DayFragment.kt b/src/com/android/calendar/DayFragment.kt index a9fb39ed..49da8bdb 100644 --- a/src/com/android/calendar/DayFragment.kt +++ b/src/com/android/calendar/DayFragment.kt @@ -13,194 +13,164 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar; - -import com.android.calendar.CalendarController.EventInfo; -import com.android.calendar.CalendarController.EventType; - -import android.app.Fragment; -import android.content.Context; -import android.os.Bundle; -import android.text.format.Time; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.ProgressBar; -import android.widget.ViewSwitcher; -import android.widget.ViewSwitcher.ViewFactory; +package com.android.calendar + +import com.android.calendar.CalendarController.EventInfo +import com.android.calendar.CalendarController.EventType +import android.app.Fragment +import android.content.Context +import android.os.Bundle +import android.text.format.Time +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams +import android.view.animation.Animation +import android.view.animation.AnimationUtils +import android.widget.ProgressBar +import android.widget.ViewSwitcher +import android.widget.ViewSwitcher.ViewFactory /** * This is the base class for Day and Week Activities. */ -public class DayFragment extends Fragment implements CalendarController.EventHandler, ViewFactory { - /** - * The view id used for all the views we create. It's OK to have all child - * views have the same ID. This ID is used to pick which view receives - * focus when a view hierarchy is saved / restore - */ - private static final int VIEW_ID = 1; - - protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time"; - - protected ProgressBar mProgressBar; - protected ViewSwitcher mViewSwitcher; - protected Animation mInAnimationForward; - protected Animation mOutAnimationForward; - protected Animation mInAnimationBackward; - protected Animation mOutAnimationBackward; - EventLoader mEventLoader; - - Time mSelectedDay = new Time(); - - private final Runnable mTZUpdater = new Runnable() { - @Override - public void run() { - if (!DayFragment.this.isAdded()) { - return; +class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { + protected var mProgressBar: ProgressBar? = null + protected var mViewSwitcher: ViewSwitcher? = null + protected var mInAnimationForward: Animation? = null + protected var mOutAnimationForward: Animation? = null + protected var mInAnimationBackward: Animation? = null + protected var mOutAnimationBackward: Animation? = null + var mEventLoader: EventLoader? = null + var mSelectedDay: Time = Time() + private val mTZUpdater: Runnable = object : Runnable { + override fun run() { + if (!this@DayFragment.isAdded()) { + return } - String tz = Utils.getTimeZone(getActivity(), mTZUpdater); - mSelectedDay.timezone = tz; - mSelectedDay.normalize(true); + val tz: String = Utils.getTimeZone(getActivity(), this) + mSelectedDay.timezone = tz + mSelectedDay.normalize(true) } - }; - - private int mNumDays; + } + private var mNumDays = 0 - public DayFragment() { - mSelectedDay.setToNow(); + constructor() { + mSelectedDay.setToNow() } - public DayFragment(long timeMillis, int numOfDays) { - mNumDays = numOfDays; - if (timeMillis == 0) { - mSelectedDay.setToNow(); + constructor(timeMillis: Long, numOfDays: Int) { + mNumDays = numOfDays + if (timeMillis == 0L) { + mSelectedDay.setToNow() } else { - mSelectedDay.set(timeMillis); + mSelectedDay.set(timeMillis) } } - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - Context context = getActivity(); - - mInAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_in); - mOutAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_out); - mInAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_in); - mOutAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_out); - - mEventLoader = new EventLoader(context); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.day_activity, null); - - mViewSwitcher = (ViewSwitcher) v.findViewById(R.id.switcher); - mViewSwitcher.setFactory(this); - mViewSwitcher.getCurrentView().requestFocus(); - ((DayView) mViewSwitcher.getCurrentView()).updateTitle(); - - return v; - } - - public View makeView() { - mTZUpdater.run(); - DayView view = new DayView(getActivity(), CalendarController - .getInstance(getActivity()), mViewSwitcher, mEventLoader, mNumDays); - view.setId(VIEW_ID); - view.setLayoutParams(new ViewSwitcher.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - view.setSelected(mSelectedDay, false, false); - return view; - } - - @Override - public void onResume() { - super.onResume(); - mEventLoader.startBackgroundThread(); - mTZUpdater.run(); - eventsChanged(); - DayView view = (DayView) mViewSwitcher.getCurrentView(); - view.handleOnResume(); - view.restartCurrentTimeUpdates(); - - view = (DayView) mViewSwitcher.getNextView(); - view.handleOnResume(); - view.restartCurrentTimeUpdates(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - } - - @Override - public void onPause() { - super.onPause(); - DayView view = (DayView) mViewSwitcher.getCurrentView(); - view.cleanup(); - view = (DayView) mViewSwitcher.getNextView(); - view.cleanup(); - mEventLoader.stopBackgroundThread(); + override fun onCreate(icicle: Bundle?) { + super.onCreate(icicle) + val context: Context = getActivity() + mInAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_in) + mOutAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_out) + mInAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_in) + mOutAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_out) + mEventLoader = EventLoader(context) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View { + val v: View = inflater.inflate(R.layout.day_activity, null) + mViewSwitcher = v.findViewById(R.id.switcher) as ViewSwitcher + mViewSwitcher?.setFactory(this) + mViewSwitcher?.getCurrentView()?.requestFocus() + (mViewSwitcher?.getCurrentView() as DayView).updateTitle() + return v + } + + override fun makeView(): View { + mTZUpdater.run() + val view = DayView(getActivity(), CalendarController + .getInstance(getActivity()), mViewSwitcher, mEventLoader, mNumDays) + view.setId(DayFragment.Companion.VIEW_ID) + view.setLayoutParams(LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)) + view.setSelected(mSelectedDay, false, false) + return view + } + + override fun onResume() { + super.onResume() + mEventLoader!!.startBackgroundThread() + mTZUpdater.run() + eventsChanged() + var view: DayView = mViewSwitcher?.getCurrentView() as DayView + view.handleOnResume() + view.restartCurrentTimeUpdates() + view = mViewSwitcher?.getNextView() as DayView + view.handleOnResume() + view.restartCurrentTimeUpdates() + } + + override fun onSaveInstanceState(outState: Bundle?) { + super.onSaveInstanceState(outState) + } + + override fun onPause() { + super.onPause() + var view: DayView = mViewSwitcher?.getCurrentView() as DayView + view.cleanup() + view = mViewSwitcher?.getNextView() as DayView + view.cleanup() + mEventLoader!!.stopBackgroundThread() // Stop events cross-fade animation - view.stopEventsAnimation(); - ((DayView) mViewSwitcher.getNextView()).stopEventsAnimation(); + view.stopEventsAnimation() + (mViewSwitcher?.getNextView() as DayView).stopEventsAnimation() } - void startProgressSpinner() { + fun startProgressSpinner() { // start the progress spinner - mProgressBar.setVisibility(View.VISIBLE); + mProgressBar?.setVisibility(View.VISIBLE) } - void stopProgressSpinner() { + fun stopProgressSpinner() { // stop the progress spinner - mProgressBar.setVisibility(View.GONE); + mProgressBar?.setVisibility(View.GONE) } - private void goTo(Time goToTime, boolean ignoreTime, boolean animateToday) { + private fun goTo(goToTime: Time?, ignoreTime: Boolean, animateToday: Boolean) { if (mViewSwitcher == null) { // The view hasn't been set yet. Just save the time and use it later. - mSelectedDay.set(goToTime); - return; + mSelectedDay.set(goToTime) + return } - - DayView currentView = (DayView) mViewSwitcher.getCurrentView(); + val currentView: DayView = mViewSwitcher?.getCurrentView() as DayView // How does goTo time compared to what's already displaying? - int diff = currentView.compareToVisibleTimeRange(goToTime); - + val diff: Int = currentView.compareToVisibleTimeRange(goToTime) if (diff == 0) { // In visible range. No need to switch view - currentView.setSelected(goToTime, ignoreTime, animateToday); + currentView.setSelected(goToTime, ignoreTime, animateToday) } else { // Figure out which way to animate if (diff > 0) { - mViewSwitcher.setInAnimation(mInAnimationForward); - mViewSwitcher.setOutAnimation(mOutAnimationForward); + mViewSwitcher?.setInAnimation(mInAnimationForward) + mViewSwitcher?.setOutAnimation(mOutAnimationForward) } else { - mViewSwitcher.setInAnimation(mInAnimationBackward); - mViewSwitcher.setOutAnimation(mOutAnimationBackward); + mViewSwitcher?.setInAnimation(mInAnimationBackward) + mViewSwitcher?.setOutAnimation(mOutAnimationBackward) } - - DayView next = (DayView) mViewSwitcher.getNextView(); + val next: DayView = mViewSwitcher?.getNextView() as DayView if (ignoreTime) { - next.setFirstVisibleHour(currentView.getFirstVisibleHour()); + next.setFirstVisibleHour(currentView.getFirstVisibleHour()) } - - next.setSelected(goToTime, ignoreTime, animateToday); - next.reloadEvents(); - mViewSwitcher.showNext(); - next.requestFocus(); - next.updateTitle(); - next.restartCurrentTimeUpdates(); + next.setSelected(goToTime, ignoreTime, animateToday) + next.reloadEvents() + mViewSwitcher?.showNext() + next.requestFocus() + next.updateTitle() + next.restartCurrentTimeUpdates() } } @@ -211,46 +181,50 @@ public class DayFragment extends Fragment implements CalendarController.EventHan * * @return the selected time in milliseconds */ - public long getSelectedTimeInMillis() { - if (mViewSwitcher == null) { - return -1; - } - DayView view = (DayView) mViewSwitcher.getCurrentView(); - if (view == null) { - return -1; + val selectedTimeInMillis: Long + get() { + if (mViewSwitcher == null) { + return -1 + } + val view: DayView = mViewSwitcher?.getCurrentView() as DayView ?: return -1 + return view.getSelectedTimeInMillis() } - return view.getSelectedTimeInMillis(); - } - public void eventsChanged() { + override fun eventsChanged() { if (mViewSwitcher == null) { - return; + return } - DayView view = (DayView) mViewSwitcher.getCurrentView(); - view.clearCachedEvents(); - view.reloadEvents(); - - view = (DayView) mViewSwitcher.getNextView(); - view.clearCachedEvents(); + var view: DayView = mViewSwitcher?.getCurrentView() as DayView + view.clearCachedEvents() + view.reloadEvents() + view = mViewSwitcher?.getNextView() as DayView + view.clearCachedEvents() } - public DayView getNextView() { - return (DayView) mViewSwitcher.getNextView(); - } - - public long getSupportedEventTypes() { - return EventType.GO_TO | EventType.EVENTS_CHANGED; - } + val nextView: DayView + get() = mViewSwitcher?.getNextView() as DayView + override val supportedEventTypes: Long + get() = CalendarController.EventType.GO_TO or CalendarController.EventType.EVENTS_CHANGED - public void handleEvent(EventInfo msg) { - if (msg.eventType == EventType.GO_TO) { + override fun handleEvent(msg: CalendarController.EventInfo?) { + if (msg?.eventType == CalendarController.EventType.GO_TO) { // TODO support a range of time // TODO support event_id // TODO support select message - goTo(msg.selectedTime, (msg.extraLong & CalendarController.EXTRA_GOTO_DATE) != 0, - (msg.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0); - } else if (msg.eventType == EventType.EVENTS_CHANGED) { - eventsChanged(); + goTo(msg?.selectedTime, msg?.extraLong and CalendarController.EXTRA_GOTO_DATE != 0L, + msg?.extraLong and CalendarController.EXTRA_GOTO_TODAY != 0L) + } else if (msg?.eventType == CalendarController.EventType.EVENTS_CHANGED) { + eventsChanged() } } -} + + companion object { + /** + * The view id used for all the views we create. It's OK to have all child + * views have the same ID. This ID is used to pick which view receives + * focus when a view hierarchy is saved / restore + */ + private const val VIEW_ID = 1 + protected const val BUNDLE_KEY_RESTORE_TIME = "key_restore_time" + } +} \ No newline at end of file -- cgit v1.2.3 From e89b9dddbf24a86a61ba713480db50a8031e13c5 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Tue, 22 Jun 2021 23:57:22 +0000 Subject: AOSP/Calendar - Add Kotlin copy of CalendarViewAdapter.java and MonthListView.kt Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: I7e75680bbba28fc5bda0815b12e6d8ceb22738ba --- src/com/android/calendar/CalendarViewAdapter.kt | 409 ++++++++++++++++++++++++ src/com/android/calendar/month/MonthListView.kt | 51 +++ 2 files changed, 460 insertions(+) create mode 100644 src/com/android/calendar/CalendarViewAdapter.kt create mode 100644 src/com/android/calendar/month/MonthListView.kt diff --git a/src/com/android/calendar/CalendarViewAdapter.kt b/src/com/android/calendar/CalendarViewAdapter.kt new file mode 100644 index 00000000..524268fc --- /dev/null +++ b/src/com/android/calendar/CalendarViewAdapter.kt @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2011 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 com.android.calendar.CalendarController.ViewType; + +import android.content.Context; +import android.os.Handler; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import java.util.Formatter; +import java.util.Locale; + + +/* + * The MenuSpinnerAdapter defines the look of the ActionBar's pull down menu + * for small screen layouts. The pull down menu replaces the tabs uses for big screen layouts + * + * The MenuSpinnerAdapter responsible for creating the views used for in the pull down menu. + */ + +public class CalendarViewAdapter extends BaseAdapter { + + private static final String TAG = "MenuSpinnerAdapter"; + + private final String mButtonNames []; // Text on buttons + + // Used to define the look of the menu button according to the current view: + // Day view: show day of the week + full date underneath + // Week view: show the month + year + // Month view: show the month + year + // Agenda view: show day of the week + full date underneath + private int mCurrentMainView; + + private final LayoutInflater mInflater; + + // Defines the types of view returned by this spinner + private static final int BUTTON_VIEW_TYPE = 0; + static final int VIEW_TYPE_NUM = 1; // Increase this if you add more view types + + public static final int DAY_BUTTON_INDEX = 0; + public static final int WEEK_BUTTON_INDEX = 1; + public static final int MONTH_BUTTON_INDEX = 2; + public static final int AGENDA_BUTTON_INDEX = 3; + + // The current selected event's time, used to calculate the date and day of the week + // for the buttons. + private long mMilliTime; + private String mTimeZone; + private long mTodayJulianDay; + + private final Context mContext; + private final Formatter mFormatter; + private final StringBuilder mStringBuilder; + private Handler mMidnightHandler = null; // Used to run a time update every midnight + private final boolean mShowDate; // Spinner mode indicator (view name or view name with date) + + // Updates time specific variables (time-zone, today's Julian day). + private final Runnable mTimeUpdater = new Runnable() { + @Override + public void run() { + refresh(mContext); + } + }; + + public CalendarViewAdapter(Context context, int viewType, boolean showDate) { + super(); + + mMidnightHandler = new Handler(); + mCurrentMainView = viewType; + mContext = context; + mShowDate = showDate; + + // Initialize + mButtonNames = context.getResources().getStringArray(R.array.buttons_list); + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mStringBuilder = new StringBuilder(50); + mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); + + // Sets time specific variables and starts a thread for midnight updates + if (showDate) { + refresh(context); + } + } + + + // Sets the time zone and today's Julian day to be used by the adapter. + // Also, notify listener on the change and resets the midnight update thread. + public void refresh(Context context) { + mTimeZone = Utils.getTimeZone(context, mTimeUpdater); + Time time = new Time(mTimeZone); + long now = System.currentTimeMillis(); + time.set(now); + mTodayJulianDay = Time.getJulianDay(now, time.gmtoff); + notifyDataSetChanged(); + setMidnightHandler(); + } + + // Sets a thread to run 1 second after midnight and update the current date + // This is used to display correctly the date of yesterday/today/tomorrow + private void setMidnightHandler() { + mMidnightHandler.removeCallbacks(mTimeUpdater); + // Set the time updater to run at 1 second after midnight + long now = System.currentTimeMillis(); + Time time = new Time(mTimeZone); + time.set(now); + long runInMillis = (24 * 3600 - time.hour * 3600 - time.minute * 60 - + time.second + 1) * 1000; + mMidnightHandler.postDelayed(mTimeUpdater, runInMillis); + } + + // Stops the midnight update thread, called by the activity when it is paused. + public void onPause() { + mMidnightHandler.removeCallbacks(mTimeUpdater); + } + + // Returns the amount of buttons in the menu + @Override + public int getCount() { + return mButtonNames.length; + } + + + @Override + public Object getItem(int position) { + if (position < mButtonNames.length) { + return mButtonNames[position]; + } + return null; + } + + @Override + public long getItemId(int position) { + // Item ID is its location in the list + return position; + } + + @Override + public boolean hasStableIds() { + return false; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + View v; + + if (mShowDate) { + // Check if can recycle the view + if (convertView == null || ((Integer) convertView.getTag()).intValue() + != R.layout.actionbar_pulldown_menu_top_button) { + v = mInflater.inflate(R.layout.actionbar_pulldown_menu_top_button, parent, false); + // Set the tag to make sure you can recycle it when you get it + // as a convert view + v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button)); + } else { + v = convertView; + } + TextView weekDay = (TextView) v.findViewById(R.id.top_button_weekday); + TextView date = (TextView) v.findViewById(R.id.top_button_date); + + switch (mCurrentMainView) { + case ViewType.DAY: + weekDay.setVisibility(View.VISIBLE); + weekDay.setText(buildDayOfWeek()); + date.setText(buildFullDate()); + break; + case ViewType.WEEK: + if (Utils.getShowWeekNumber(mContext)) { + weekDay.setVisibility(View.VISIBLE); + weekDay.setText(buildWeekNum()); + } else { + weekDay.setVisibility(View.GONE); + } + date.setText(buildMonthYearDate()); + break; + case ViewType.MONTH: + weekDay.setVisibility(View.GONE); + date.setText(buildMonthYearDate()); + break; + default: + v = null; + break; + } + } else { + if (convertView == null || ((Integer) convertView.getTag()).intValue() + != R.layout.actionbar_pulldown_menu_top_button_no_date) { + v = mInflater.inflate( + R.layout.actionbar_pulldown_menu_top_button_no_date, parent, false); + // Set the tag to make sure you can recycle it when you get it + // as a convert view + v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button_no_date)); + } else { + v = convertView; + } + TextView title = (TextView) v; + switch (mCurrentMainView) { + case ViewType.DAY: + title.setText(mButtonNames [DAY_BUTTON_INDEX]); + break; + case ViewType.WEEK: + title.setText(mButtonNames [WEEK_BUTTON_INDEX]); + break; + case ViewType.MONTH: + title.setText(mButtonNames [MONTH_BUTTON_INDEX]); + break; + default: + v = null; + break; + } + } + return v; + } + + @Override + public int getItemViewType(int position) { + // Only one kind of view is used + return BUTTON_VIEW_TYPE; + } + + @Override + public int getViewTypeCount() { + return VIEW_TYPE_NUM; + } + + @Override + public boolean isEmpty() { + return (mButtonNames.length == 0); + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + View v = mInflater.inflate(R.layout.actionbar_pulldown_menu_button, parent, false); + TextView viewType = (TextView)v.findViewById(R.id.button_view); + TextView date = (TextView)v.findViewById(R.id.button_date); + switch (position) { + case DAY_BUTTON_INDEX: + viewType.setText(mButtonNames [DAY_BUTTON_INDEX]); + if (mShowDate) { + date.setText(buildMonthDayDate()); + } + break; + case WEEK_BUTTON_INDEX: + viewType.setText(mButtonNames [WEEK_BUTTON_INDEX]); + if (mShowDate) { + date.setText(buildWeekDate()); + } + break; + case MONTH_BUTTON_INDEX: + viewType.setText(mButtonNames [MONTH_BUTTON_INDEX]); + if (mShowDate) { + date.setText(buildMonthDate()); + } + break; + default: + v = convertView; + break; + } + return v; + } + + // Updates the current viewType + // Used to match the label on the menu button with the calendar view + public void setMainView(int viewType) { + mCurrentMainView = viewType; + notifyDataSetChanged(); + } + + // Update the date that is displayed on buttons + // Used when the user selects a new day/week/month to watch + public void setTime(long time) { + mMilliTime = time; + notifyDataSetChanged(); + } + + // Builds a string with the day of the week and the word yesterday/today/tomorrow + // before it if applicable. + private String buildDayOfWeek() { + + Time t = new Time(mTimeZone); + t.set(mMilliTime); + long julianDay = Time.getJulianDay(mMilliTime,t.gmtoff); + String dayOfWeek = null; + mStringBuilder.setLength(0); + + if (julianDay == mTodayJulianDay) { + dayOfWeek = mContext.getString(R.string.agenda_today, + DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, + DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()); + } else if (julianDay == mTodayJulianDay - 1) { + dayOfWeek = mContext.getString(R.string.agenda_yesterday, + DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, + DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()); + } else if (julianDay == mTodayJulianDay + 1) { + dayOfWeek = mContext.getString(R.string.agenda_tomorrow, + DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, + DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()); + } else { + dayOfWeek = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, + DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString(); + } + return dayOfWeek.toUpperCase(); + } + + // Builds strings with different formats: + // Full date: Month,day Year + // Month year + // Month day + // Month + // Week: month day-day or month day - month day + private String buildFullDate() { + mStringBuilder.setLength(0); + String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, + DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString(); + return date; + } + + private String buildMonthYearDate() { + mStringBuilder.setLength(0); + String date = DateUtils.formatDateRange( + mContext, + mFormatter, + mMilliTime, + mMilliTime, + DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY + | DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString(); + return date; + } + + private String buildMonthDayDate() { + mStringBuilder.setLength(0); + String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, + DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR, mTimeZone).toString(); + return date; + } + + private String buildMonthDate() { + mStringBuilder.setLength(0); + String date = DateUtils.formatDateRange( + mContext, + mFormatter, + mMilliTime, + mMilliTime, + DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR + | DateUtils.FORMAT_NO_MONTH_DAY, mTimeZone).toString(); + return date; + } + + private String buildWeekDate() { + // Calculate the start of the week, taking into account the "first day of the week" + // setting. + + Time t = new Time(mTimeZone); + t.set(mMilliTime); + int firstDayOfWeek = Utils.getFirstDayOfWeek(mContext); + int dayOfWeek = t.weekDay; + int diff = dayOfWeek - firstDayOfWeek; + if (diff != 0) { + if (diff < 0) { + diff += 7; + } + t.monthDay -= diff; + t.normalize(true /* ignore isDst */); + } + + long weekStartTime = t.toMillis(true); + // The end of the week is 6 days after the start of the week + long weekEndTime = weekStartTime + DateUtils.WEEK_IN_MILLIS - DateUtils.DAY_IN_MILLIS; + + // If week start and end is in 2 different months, use short months names + Time t1 = new Time(mTimeZone); + t.set(weekEndTime); + int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR; + if (t.month != t1.month) { + flags |= DateUtils.FORMAT_ABBREV_MONTH; + } + + mStringBuilder.setLength(0); + String date = DateUtils.formatDateRange(mContext, mFormatter, weekStartTime, + weekEndTime, flags, mTimeZone).toString(); + return date; + } + + private String buildWeekNum() { + int week = Utils.getWeekNumberFromTime(mMilliTime, mContext); + return mContext.getResources().getQuantityString(R.plurals.weekN, week, week); + } + +} diff --git a/src/com/android/calendar/month/MonthListView.kt b/src/com/android/calendar/month/MonthListView.kt new file mode 100644 index 00000000..f2621ccb --- /dev/null +++ b/src/com/android/calendar/month/MonthListView.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 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.month; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.ListView; + +import com.android.calendar.Utils; + +public class MonthListView extends ListView { + + private static final String TAG = "MonthListView"; + + public MonthListView(Context context) { + super(context); + } + + public MonthListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public MonthListView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + return super.onTouchEvent(ev); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return super.onInterceptTouchEvent(ev); + } +} -- cgit v1.2.3 From 0f56a6c9c1a4fb4343823c7dadd3c21af4a28235 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Wed, 23 Jun 2021 00:11:49 +0000 Subject: AOSP/Calendar - Add Kotlin code for CalendarViewAdapter.kt and MonthListView.kt. Also uploaded corresponding Android.bp file No major changes in this file. A few functions required override modifiers and a few variable declarations to include nullability references. I also changed the initialization of mContext Test: manual (ran build and unit tests) and CTS testing suite $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.136 OK (22 tests) CTS TESTING: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ============================================ ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 10m 3s Total aggregated tests run time: 10m 3s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.62 tests/sec [376 tests / 603720 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 10845 ms || clean = 3384 ms Total preparation time: 10s || Total tear down time: 3s ======================================================= =============== Summary =============== Total Run time: 16m 55s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: Iadb062ccea3b3405f005bc79536e6e97ca6f6947 --- Android.bp | 2 + src/com/android/calendar/CalendarViewAdapter.kt | 462 +++++++++++------------- src/com/android/calendar/month/MonthListView.kt | 48 +-- 3 files changed, 234 insertions(+), 278 deletions(-) diff --git a/Android.bp b/Android.bp index cc2be4b1..8cf5c198 100644 --- a/Android.bp +++ b/Android.bp @@ -33,10 +33,12 @@ exclude_srcsd = [ ] exclude_srcsm = [ + "src/**/calendar/month/MonthListView.java", "src/**/calendar/AsyncQueryServiceHelper.java", "src/**/calendar/CalendarApplication.java", "src/**/calendar/CalendarBackupAgent.java", "src/**/calendar/CalendarUtils.java", + "src/**/calendar/CalendarViewAdapter.java", "src/**/calendar/DayFragment.java", "src/**/calendar/EventGeometry.java", "src/**/calendar/EventLoader.java", diff --git a/src/com/android/calendar/CalendarViewAdapter.kt b/src/com/android/calendar/CalendarViewAdapter.kt index 524268fc..920eec2e 100644 --- a/src/com/android/calendar/CalendarViewAdapter.kt +++ b/src/com/android/calendar/CalendarViewAdapter.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,24 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar; - -import com.android.calendar.CalendarController.ViewType; - -import android.content.Context; -import android.os.Handler; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.TextView; - -import java.util.Formatter; -import java.util.Locale; - +package com.android.calendar + +import com.android.calendar.CalendarController.ViewType +import android.content.Context +import android.os.Handler +import android.text.format.DateUtils +import android.text.format.Time +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.TextView +import java.util.Formatter +import java.util.Locale /* * The MenuSpinnerAdapter defines the look of the ActionBar's pull down menu @@ -38,288 +34,234 @@ import java.util.Locale; * * The MenuSpinnerAdapter responsible for creating the views used for in the pull down menu. */ - -public class CalendarViewAdapter extends BaseAdapter { - - private static final String TAG = "MenuSpinnerAdapter"; - - private final String mButtonNames []; // Text on buttons +class CalendarViewAdapter(context: Context, viewType: Int, showDate: Boolean) : BaseAdapter() { + private val mButtonNames: Array // Text on buttons // Used to define the look of the menu button according to the current view: // Day view: show day of the week + full date underneath // Week view: show the month + year // Month view: show the month + year // Agenda view: show day of the week + full date underneath - private int mCurrentMainView; - - private final LayoutInflater mInflater; - - // Defines the types of view returned by this spinner - private static final int BUTTON_VIEW_TYPE = 0; - static final int VIEW_TYPE_NUM = 1; // Increase this if you add more view types - - public static final int DAY_BUTTON_INDEX = 0; - public static final int WEEK_BUTTON_INDEX = 1; - public static final int MONTH_BUTTON_INDEX = 2; - public static final int AGENDA_BUTTON_INDEX = 3; + private var mCurrentMainView: Int + private val mInflater: LayoutInflater // The current selected event's time, used to calculate the date and day of the week // for the buttons. - private long mMilliTime; - private String mTimeZone; - private long mTodayJulianDay; - - private final Context mContext; - private final Formatter mFormatter; - private final StringBuilder mStringBuilder; - private Handler mMidnightHandler = null; // Used to run a time update every midnight - private final boolean mShowDate; // Spinner mode indicator (view name or view name with date) + private var mMilliTime: Long = 0 + private var mTimeZone: String? = null + private var mTodayJulianDay: Long = 0 + private val mContext: Context = context + private val mFormatter: Formatter + private val mStringBuilder: StringBuilder + private var mMidnightHandler: Handler? = null // Used to run a time update every midnight + private val mShowDate // Spinner mode indicator (view name or view name with date) + : Boolean // Updates time specific variables (time-zone, today's Julian day). - private final Runnable mTimeUpdater = new Runnable() { + private val mTimeUpdater: Runnable = object : Runnable { @Override - public void run() { - refresh(mContext); - } - }; - - public CalendarViewAdapter(Context context, int viewType, boolean showDate) { - super(); - - mMidnightHandler = new Handler(); - mCurrentMainView = viewType; - mContext = context; - mShowDate = showDate; - - // Initialize - mButtonNames = context.getResources().getStringArray(R.array.buttons_list); - mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mStringBuilder = new StringBuilder(50); - mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); - - // Sets time specific variables and starts a thread for midnight updates - if (showDate) { - refresh(context); + override fun run() { + refresh(mContext) } } - // Sets the time zone and today's Julian day to be used by the adapter. // Also, notify listener on the change and resets the midnight update thread. - public void refresh(Context context) { - mTimeZone = Utils.getTimeZone(context, mTimeUpdater); - Time time = new Time(mTimeZone); - long now = System.currentTimeMillis(); - time.set(now); - mTodayJulianDay = Time.getJulianDay(now, time.gmtoff); - notifyDataSetChanged(); - setMidnightHandler(); + fun refresh(context: Context?) { + mTimeZone = Utils.getTimeZone(context, mTimeUpdater) + val time = Time(mTimeZone) + val now: Long = System.currentTimeMillis() + time.set(now) + mTodayJulianDay = Time.getJulianDay(now, time.gmtoff) as Long + notifyDataSetChanged() + setMidnightHandler() } // Sets a thread to run 1 second after midnight and update the current date // This is used to display correctly the date of yesterday/today/tomorrow - private void setMidnightHandler() { - mMidnightHandler.removeCallbacks(mTimeUpdater); + private fun setMidnightHandler() { + mMidnightHandler?.removeCallbacks(mTimeUpdater) // Set the time updater to run at 1 second after midnight - long now = System.currentTimeMillis(); - Time time = new Time(mTimeZone); - time.set(now); - long runInMillis = (24 * 3600 - time.hour * 3600 - time.minute * 60 - - time.second + 1) * 1000; - mMidnightHandler.postDelayed(mTimeUpdater, runInMillis); + val now: Long = System.currentTimeMillis() + val time = Time(mTimeZone) + time.set(now) + val runInMillis: Long = ((24 * 3600 - time.hour * 3600 - time.minute * 60 - + time.second + 1) * 1000) as Long + mMidnightHandler?.postDelayed(mTimeUpdater, runInMillis) } // Stops the midnight update thread, called by the activity when it is paused. - public void onPause() { - mMidnightHandler.removeCallbacks(mTimeUpdater); + fun onPause() { + mMidnightHandler?.removeCallbacks(mTimeUpdater) } // Returns the amount of buttons in the menu @Override - public int getCount() { - return mButtonNames.length; + override fun getCount(): Int { + return mButtonNames.size } - @Override - public Object getItem(int position) { - if (position < mButtonNames.length) { - return mButtonNames[position]; - } - return null; + override fun getItem(position: Int): Any? { + return if (position < mButtonNames.size) { + mButtonNames[position] + } else null } @Override - public long getItemId(int position) { + override fun getItemId(position: Int): Long { // Item ID is its location in the list - return position; + return position.toLong() } @Override - public boolean hasStableIds() { - return false; + override fun hasStableIds(): Boolean { + return false } @Override - public View getView(int position, View convertView, ViewGroup parent) { - - View v; - + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? { + var v: View? if (mShowDate) { // Check if can recycle the view - if (convertView == null || ((Integer) convertView.getTag()).intValue() - != R.layout.actionbar_pulldown_menu_top_button) { - v = mInflater.inflate(R.layout.actionbar_pulldown_menu_top_button, parent, false); + if (convertView == null || (convertView.getTag() as Int) + != R.layout.actionbar_pulldown_menu_top_button as Int) { + v = mInflater.inflate(R.layout.actionbar_pulldown_menu_top_button, parent, false) // Set the tag to make sure you can recycle it when you get it // as a convert view - v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button)); + v.setTag(Integer(R.layout.actionbar_pulldown_menu_top_button)) } else { - v = convertView; + v = convertView } - TextView weekDay = (TextView) v.findViewById(R.id.top_button_weekday); - TextView date = (TextView) v.findViewById(R.id.top_button_date); - - switch (mCurrentMainView) { - case ViewType.DAY: - weekDay.setVisibility(View.VISIBLE); - weekDay.setText(buildDayOfWeek()); - date.setText(buildFullDate()); - break; - case ViewType.WEEK: + val weekDay: TextView = v?.findViewById(R.id.top_button_weekday) as TextView + val date: TextView = v?.findViewById(R.id.top_button_date) as TextView + when (mCurrentMainView) { + ViewType.DAY -> { + weekDay.setVisibility(View.VISIBLE) + weekDay.setText(buildDayOfWeek()) + date.setText(buildFullDate()) + } + ViewType.WEEK -> { if (Utils.getShowWeekNumber(mContext)) { - weekDay.setVisibility(View.VISIBLE); - weekDay.setText(buildWeekNum()); + weekDay.setVisibility(View.VISIBLE) + weekDay.setText(buildWeekNum()) } else { - weekDay.setVisibility(View.GONE); + weekDay.setVisibility(View.GONE) } - date.setText(buildMonthYearDate()); - break; - case ViewType.MONTH: - weekDay.setVisibility(View.GONE); - date.setText(buildMonthYearDate()); - break; - default: - v = null; - break; + date.setText(buildMonthYearDate()) + } + ViewType.MONTH -> { + weekDay.setVisibility(View.GONE) + date.setText(buildMonthYearDate()) + } + else -> v = null } } else { - if (convertView == null || ((Integer) convertView.getTag()).intValue() - != R.layout.actionbar_pulldown_menu_top_button_no_date) { + if (convertView == null || (convertView.getTag() as Int) + != R.layout.actionbar_pulldown_menu_top_button_no_date as Int) { v = mInflater.inflate( - R.layout.actionbar_pulldown_menu_top_button_no_date, parent, false); + R.layout.actionbar_pulldown_menu_top_button_no_date, parent, false) // Set the tag to make sure you can recycle it when you get it // as a convert view - v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button_no_date)); + v.setTag(Integer(R.layout.actionbar_pulldown_menu_top_button_no_date)) } else { - v = convertView; + v = convertView } - TextView title = (TextView) v; - switch (mCurrentMainView) { - case ViewType.DAY: - title.setText(mButtonNames [DAY_BUTTON_INDEX]); - break; - case ViewType.WEEK: - title.setText(mButtonNames [WEEK_BUTTON_INDEX]); - break; - case ViewType.MONTH: - title.setText(mButtonNames [MONTH_BUTTON_INDEX]); - break; - default: - v = null; - break; + val title: TextView? = v as TextView? + when (mCurrentMainView) { + ViewType.DAY -> title?.setText(mButtonNames[DAY_BUTTON_INDEX]) + ViewType.WEEK -> title?.setText(mButtonNames[WEEK_BUTTON_INDEX]) + ViewType.MONTH -> title?.setText(mButtonNames[MONTH_BUTTON_INDEX]) + else -> v = null } } - return v; + return v } @Override - public int getItemViewType(int position) { + override fun getItemViewType(position: Int): Int { // Only one kind of view is used - return BUTTON_VIEW_TYPE; + return BUTTON_VIEW_TYPE } @Override - public int getViewTypeCount() { - return VIEW_TYPE_NUM; + override fun getViewTypeCount(): Int { + return VIEW_TYPE_NUM } @Override - public boolean isEmpty() { - return (mButtonNames.length == 0); + override fun isEmpty(): Boolean { + return mButtonNames.size == 0 } @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - View v = mInflater.inflate(R.layout.actionbar_pulldown_menu_button, parent, false); - TextView viewType = (TextView)v.findViewById(R.id.button_view); - TextView date = (TextView)v.findViewById(R.id.button_date); - switch (position) { - case DAY_BUTTON_INDEX: - viewType.setText(mButtonNames [DAY_BUTTON_INDEX]); + override fun getDropDownView(position: Int, convertView: View, parent: ViewGroup?): View { + var v: View = mInflater.inflate(R.layout.actionbar_pulldown_menu_button, parent, false) + val viewType: TextView = v.findViewById(R.id.button_view) as TextView + val date: TextView = v.findViewById(R.id.button_date) as TextView + when (position) { + DAY_BUTTON_INDEX -> { + viewType.setText(mButtonNames[DAY_BUTTON_INDEX]) if (mShowDate) { - date.setText(buildMonthDayDate()); + date.setText(buildMonthDayDate()) } - break; - case WEEK_BUTTON_INDEX: - viewType.setText(mButtonNames [WEEK_BUTTON_INDEX]); + } + WEEK_BUTTON_INDEX -> { + viewType.setText(mButtonNames[WEEK_BUTTON_INDEX]) if (mShowDate) { - date.setText(buildWeekDate()); + date.setText(buildWeekDate()) } - break; - case MONTH_BUTTON_INDEX: - viewType.setText(mButtonNames [MONTH_BUTTON_INDEX]); + } + MONTH_BUTTON_INDEX -> { + viewType.setText(mButtonNames[MONTH_BUTTON_INDEX]) if (mShowDate) { - date.setText(buildMonthDate()); + date.setText(buildMonthDate()) } - break; - default: - v = convertView; - break; + } + else -> v = convertView } - return v; + return v } // Updates the current viewType // Used to match the label on the menu button with the calendar view - public void setMainView(int viewType) { - mCurrentMainView = viewType; - notifyDataSetChanged(); + fun setMainView(viewType: Int) { + mCurrentMainView = viewType + notifyDataSetChanged() } // Update the date that is displayed on buttons // Used when the user selects a new day/week/month to watch - public void setTime(long time) { - mMilliTime = time; - notifyDataSetChanged(); + fun setTime(time: Long) { + mMilliTime = time + notifyDataSetChanged() } // Builds a string with the day of the week and the word yesterday/today/tomorrow // before it if applicable. - private String buildDayOfWeek() { - - Time t = new Time(mTimeZone); - t.set(mMilliTime); - long julianDay = Time.getJulianDay(mMilliTime,t.gmtoff); - String dayOfWeek = null; - mStringBuilder.setLength(0); - - if (julianDay == mTodayJulianDay) { - dayOfWeek = mContext.getString(R.string.agenda_today, + private fun buildDayOfWeek(): String { + val t = Time(mTimeZone) + t.set(mMilliTime) + val julianDay: Long = Time.getJulianDay(mMilliTime, t.gmtoff) as Long + var dayOfWeek: String? = null + mStringBuilder.setLength(0) + dayOfWeek = if (julianDay == mTodayJulianDay) { + mContext.getString(R.string.agenda_today, DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()); + DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()) } else if (julianDay == mTodayJulianDay - 1) { - dayOfWeek = mContext.getString(R.string.agenda_yesterday, + mContext.getString(R.string.agenda_yesterday, DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()); + DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()) } else if (julianDay == mTodayJulianDay + 1) { - dayOfWeek = mContext.getString(R.string.agenda_tomorrow, + mContext.getString(R.string.agenda_tomorrow, DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()); + DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()) } else { - dayOfWeek = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString(); + DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, + DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString() } - return dayOfWeek.toUpperCase(); + return dayOfWeek.toUpperCase() } // Builds strings with different formats: @@ -328,82 +270,102 @@ public class CalendarViewAdapter extends BaseAdapter { // Month day // Month // Week: month day-day or month day - month day - private String buildFullDate() { - mStringBuilder.setLength(0); - String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString(); - return date; + private fun buildFullDate(): String { + mStringBuilder.setLength(0) + return DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, + DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString() } - private String buildMonthYearDate() { - mStringBuilder.setLength(0); - String date = DateUtils.formatDateRange( + private fun buildMonthYearDate(): String { + mStringBuilder.setLength(0) + return DateUtils.formatDateRange( mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY - | DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString(); - return date; + DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_MONTH_DAY + or DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString() } - private String buildMonthDayDate() { - mStringBuilder.setLength(0); - String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR, mTimeZone).toString(); - return date; + private fun buildMonthDayDate(): String { + mStringBuilder.setLength(0) + return DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, + DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR, mTimeZone).toString() } - private String buildMonthDate() { - mStringBuilder.setLength(0); - String date = DateUtils.formatDateRange( + private fun buildMonthDate(): String { + mStringBuilder.setLength(0) + return DateUtils.formatDateRange( mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR - | DateUtils.FORMAT_NO_MONTH_DAY, mTimeZone).toString(); - return date; + DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR + or DateUtils.FORMAT_NO_MONTH_DAY, mTimeZone).toString() } - private String buildWeekDate() { + private fun buildWeekDate(): String { // Calculate the start of the week, taking into account the "first day of the week" // setting. - - Time t = new Time(mTimeZone); - t.set(mMilliTime); - int firstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - int dayOfWeek = t.weekDay; - int diff = dayOfWeek - firstDayOfWeek; + val t = Time(mTimeZone) + t.set(mMilliTime) + val firstDayOfWeek: Int = Utils.getFirstDayOfWeek(mContext) + val dayOfWeek: Int = t.weekDay + var diff = dayOfWeek - firstDayOfWeek if (diff != 0) { if (diff < 0) { - diff += 7; + diff += 7 } - t.monthDay -= diff; - t.normalize(true /* ignore isDst */); + t.monthDay -= diff + t.normalize(true /* ignore isDst */) } - - long weekStartTime = t.toMillis(true); + val weekStartTime: Long = t.toMillis(true) // The end of the week is 6 days after the start of the week - long weekEndTime = weekStartTime + DateUtils.WEEK_IN_MILLIS - DateUtils.DAY_IN_MILLIS; + val weekEndTime: Long = weekStartTime + DateUtils.WEEK_IN_MILLIS - DateUtils.DAY_IN_MILLIS // If week start and end is in 2 different months, use short months names - Time t1 = new Time(mTimeZone); - t.set(weekEndTime); - int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR; - if (t.month != t1.month) { - flags |= DateUtils.FORMAT_ABBREV_MONTH; + val t1 = Time(mTimeZone) + t.set(weekEndTime) + var flags: Int = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR + if (t.month !== t1.month) { + flags = flags or DateUtils.FORMAT_ABBREV_MONTH } + mStringBuilder.setLength(0) + return DateUtils.formatDateRange(mContext, mFormatter, weekStartTime, + weekEndTime, flags, mTimeZone).toString() + } - mStringBuilder.setLength(0); - String date = DateUtils.formatDateRange(mContext, mFormatter, weekStartTime, - weekEndTime, flags, mTimeZone).toString(); - return date; + private fun buildWeekNum(): String { + val week: Int = Utils.getWeekNumberFromTime(mMilliTime, mContext) + return mContext.getResources().getQuantityString(R.plurals.weekN, week, week) } - private String buildWeekNum() { - int week = Utils.getWeekNumberFromTime(mMilliTime, mContext); - return mContext.getResources().getQuantityString(R.plurals.weekN, week, week); + companion object { + private const val TAG = "MenuSpinnerAdapter" + + // Defines the types of view returned by this spinner + private const val BUTTON_VIEW_TYPE = 0 + const val VIEW_TYPE_NUM = 1 // Increase this if you add more view types + const val DAY_BUTTON_INDEX = 0 + const val WEEK_BUTTON_INDEX = 1 + const val MONTH_BUTTON_INDEX = 2 + const val AGENDA_BUTTON_INDEX = 3 } -} + init { + mMidnightHandler = Handler() + mCurrentMainView = viewType + mShowDate = showDate + + // Initialize + mButtonNames = context.getResources().getStringArray(R.array.buttons_list) + mInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + mStringBuilder = StringBuilder(50) + mFormatter = Formatter(mStringBuilder, Locale.getDefault()) + + // Sets time specific variables and starts a thread for midnight updates + if (showDate) { + refresh(context) + } + } +} \ No newline at end of file diff --git a/src/com/android/calendar/month/MonthListView.kt b/src/com/android/calendar/month/MonthListView.kt index f2621ccb..1facb4c0 100644 --- a/src/com/android/calendar/month/MonthListView.kt +++ b/src/com/android/calendar/month/MonthListView.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,39 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar.month -package com.android.calendar.month; +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import android.widget.ListView +import com.android.calendar.Utils -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.ListView; +class MonthListView : ListView { + constructor(context: Context?) : super(context) {} + constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : + super(context, attrs, defStyle) {} + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {} -import com.android.calendar.Utils; - -public class MonthListView extends ListView { - - private static final String TAG = "MonthListView"; - - public MonthListView(Context context) { - super(context); - } - - public MonthListView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public MonthListView(Context context, AttributeSet attrs) { - super(context, attrs); + @Override + override fun onTouchEvent(ev: MotionEvent?): Boolean { + return super.onTouchEvent(ev) } @Override - public boolean onTouchEvent(MotionEvent ev) { - return super.onTouchEvent(ev); + override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { + return super.onInterceptTouchEvent(ev) } - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return super.onInterceptTouchEvent(ev); + companion object { + private const val TAG = "MonthListView" } -} +} \ No newline at end of file -- cgit v1.2.3 From 00b027ec4538966eb2116bc7e6588cd7a7b5dd46 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Tue, 22 Jun 2021 14:57:00 +0000 Subject: AOSP/Calendar - Copy of Event.java The Java code in Event.java has been copied into a corresponding .kt file. Test: manual - opening both .java and .kt files shows that they are identical. Change-Id: Ic03ca7337d4eaeb58ee7c87c07e64c6bbbb5d7c5 --- src/com/android/calendar/Event.kt | 642 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 642 insertions(+) create mode 100644 src/com/android/calendar/Event.kt diff --git a/src/com/android/calendar/Event.kt b/src/com/android/calendar/Event.kt new file mode 100644 index 00000000..095e43e7 --- /dev/null +++ b/src/com/android/calendar/Event.kt @@ -0,0 +1,642 @@ +/* + * 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.ContentUris; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.database.Cursor; +import android.net.Uri; +import android.os.Debug; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Events; +import android.provider.CalendarContract.Instances; +import android.text.TextUtils; +import android.text.format.DateUtils; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicInteger; + +// TODO: should Event be Parcelable so it can be passed via Intents? +public class Event implements Cloneable { + + private static final String TAG = "CalEvent"; + private static final boolean PROFILE = false; + + /** + * The sort order is: + * 1) events with an earlier start (begin for normal events, startday for allday) + * 2) events with a later end (end for normal events, endday for allday) + * 3) the title (unnecessary, but nice) + * + * The start and end day is sorted first so that all day events are + * sorted correctly with respect to events that are >24 hours (and + * therefore show up in the allday area). + */ + private static final String SORT_EVENTS_BY = + "begin ASC, end DESC, title ASC"; + private static final String SORT_ALLDAY_BY = + "startDay ASC, endDay DESC, title ASC"; + private static final String DISPLAY_AS_ALLDAY = "dispAllday"; + + private static final String EVENTS_WHERE = DISPLAY_AS_ALLDAY + "=0"; + private static final String ALLDAY_WHERE = DISPLAY_AS_ALLDAY + "=1"; + + // The projection to use when querying instances to build a list of events + public static final String[] EVENT_PROJECTION = new String[] { + Instances.TITLE, // 0 + Instances.EVENT_LOCATION, // 1 + Instances.ALL_DAY, // 2 + Instances.DISPLAY_COLOR, // 3 If SDK < 16, set to Instances.CALENDAR_COLOR. + Instances.EVENT_TIMEZONE, // 4 + Instances.EVENT_ID, // 5 + Instances.BEGIN, // 6 + Instances.END, // 7 + Instances._ID, // 8 + Instances.START_DAY, // 9 + Instances.END_DAY, // 10 + Instances.START_MINUTE, // 11 + Instances.END_MINUTE, // 12 + Instances.HAS_ALARM, // 13 + Instances.RRULE, // 14 + Instances.RDATE, // 15 + Instances.SELF_ATTENDEE_STATUS, // 16 + Events.ORGANIZER, // 17 + Events.GUESTS_CAN_MODIFY, // 18 + Instances.ALL_DAY + "=1 OR (" + Instances.END + "-" + Instances.BEGIN + ")>=" + + DateUtils.DAY_IN_MILLIS + " AS " + DISPLAY_AS_ALLDAY, // 19 + }; + + // The indices for the projection array above. + private static final int PROJECTION_TITLE_INDEX = 0; + private static final int PROJECTION_LOCATION_INDEX = 1; + private static final int PROJECTION_ALL_DAY_INDEX = 2; + private static final int PROJECTION_COLOR_INDEX = 3; + private static final int PROJECTION_TIMEZONE_INDEX = 4; + private static final int PROJECTION_EVENT_ID_INDEX = 5; + private static final int PROJECTION_BEGIN_INDEX = 6; + private static final int PROJECTION_END_INDEX = 7; + private static final int PROJECTION_START_DAY_INDEX = 9; + private static final int PROJECTION_END_DAY_INDEX = 10; + private static final int PROJECTION_START_MINUTE_INDEX = 11; + private static final int PROJECTION_END_MINUTE_INDEX = 12; + private static final int PROJECTION_HAS_ALARM_INDEX = 13; + 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; + private static final int PROJECTION_DISPLAY_AS_ALLDAY = 19; + + static { + if (!Utils.isJellybeanOrLater()) { + EVENT_PROJECTION[PROJECTION_COLOR_INDEX] = Instances.CALENDAR_COLOR; + } + } + + private static String mNoTitleString; + private static int mNoColorColor; + + 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 + public int startTime; // Start and end time are in minutes since midnight + public int endTime; + + public long startMillis; // UTC milliseconds since the epoch + public long endMillis; // UTC milliseconds since the epoch + private int mColumn; + private int mMaxColumns; + + public boolean hasAlarm; + public boolean isRepeating; + + public int selfAttendeeStatus; + + // The coordinates of the event rectangle drawn on the screen. + public float left; + public float right; + public float top; + public float bottom; + + // These 4 fields are used for navigating among events within the selected + // hour in the Day and Week view. + public Event nextRight; + public Event nextLeft; + public Event nextUp; + public Event nextDown; + + @Override + public final Object clone() throws CloneNotSupportedException { + super.clone(); + Event e = new Event(); + + e.title = title; + e.color = color; + e.location = location; + e.allDay = allDay; + e.startDay = startDay; + e.endDay = endDay; + e.startTime = startTime; + e.endTime = endTime; + e.startMillis = startMillis; + e.endMillis = endMillis; + e.hasAlarm = hasAlarm; + e.isRepeating = isRepeating; + e.selfAttendeeStatus = selfAttendeeStatus; + e.organizer = organizer; + e.guestsCanModify = guestsCanModify; + + return e; + } + + public final void copyTo(Event dest) { + dest.id = id; + dest.title = title; + dest.color = color; + dest.location = location; + dest.allDay = allDay; + dest.startDay = startDay; + dest.endDay = endDay; + dest.startTime = startTime; + dest.endTime = endTime; + dest.startMillis = startMillis; + dest.endMillis = endMillis; + dest.hasAlarm = hasAlarm; + dest.isRepeating = isRepeating; + dest.selfAttendeeStatus = selfAttendeeStatus; + dest.organizer = organizer; + dest.guestsCanModify = guestsCanModify; + } + + public static final Event newInstance() { + Event e = new Event(); + + e.id = 0; + e.title = null; + e.color = 0; + e.location = null; + e.allDay = false; + e.startDay = 0; + e.endDay = 0; + e.startTime = 0; + e.endTime = 0; + e.startMillis = 0; + e.endMillis = 0; + e.hasAlarm = false; + e.isRepeating = false; + e.selfAttendeeStatus = Attendees.ATTENDEE_STATUS_NONE; + + return e; + } + + /** + * Loads days days worth of instances starting at startDay. + */ + public static void loadEvents(Context context, ArrayList events, int startDay, int days, + int requestId, AtomicInteger sequenceNumber) { + + if (PROFILE) { + Debug.startMethodTracing("loadEvents"); + } + + Cursor cEvents = null; + Cursor cAllday = null; + + events.clear(); + try { + int endDay = startDay + days - 1; + + // We use the byDay instances query to get a list of all events for + // the days we're interested in. + // The sort order is: events with an earlier start time occur + // first and if the start times are the same, then events with + // a later end time occur first. The later end time is ordered + // first so that long rectangles in the calendar views appear on + // the left side. If the start and end times of two events are + // the same then we sort alphabetically on the title. This isn't + // required for correctness, it just adds a nice touch. + + // Respect the preference to show/hide declined events + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + boolean hideDeclined = prefs.getBoolean(GeneralPreferences.KEY_HIDE_DECLINED, + false); + + String where = EVENTS_WHERE; + String whereAllday = ALLDAY_WHERE; + if (hideDeclined) { + String hideString = " AND " + Instances.SELF_ATTENDEE_STATUS + "!=" + + Attendees.ATTENDEE_STATUS_DECLINED; + where += hideString; + whereAllday += hideString; + } + + cEvents = instancesQuery(context.getContentResolver(), EVENT_PROJECTION, startDay, + endDay, where, null, SORT_EVENTS_BY); + cAllday = instancesQuery(context.getContentResolver(), EVENT_PROJECTION, startDay, + endDay, whereAllday, null, SORT_ALLDAY_BY); + + // Check if we should return early because there are more recent + // load requests waiting. + if (requestId != sequenceNumber.get()) { + return; + } + + buildEventsFromCursor(events, cEvents, context, startDay, endDay); + buildEventsFromCursor(events, cAllday, context, startDay, endDay); + + } finally { + if (cEvents != null) { + cEvents.close(); + } + if (cAllday != null) { + cAllday.close(); + } + if (PROFILE) { + Debug.stopMethodTracing(); + } + } + } + + /** + * Performs a query to return all visible instances in the given range + * that match the given selection. This is a blocking function and + * should not be done on the UI thread. This will cause an expansion of + * recurring events to fill this time range if they are not already + * expanded and will slow down for larger time ranges with many + * recurring events. + * + * @param cr The ContentResolver to use for the query + * @param projection The columns to return + * @param begin The start of the time range to query in UTC millis since + * epoch + * @param end The end of the time range to query in UTC millis since + * epoch + * @param selection Filter on the query as an SQL WHERE statement + * @param selectionArgs Args to replace any '?'s in the selection + * @param orderBy How to order the rows as an SQL ORDER BY statement + * @return A Cursor of instances matching the selection + */ + private static final Cursor instancesQuery(ContentResolver cr, String[] projection, + int startDay, int endDay, String selection, String[] selectionArgs, String orderBy) { + String WHERE_CALENDARS_SELECTED = Calendars.VISIBLE + "=?"; + String[] WHERE_CALENDARS_ARGS = {"1"}; + String DEFAULT_SORT_ORDER = "begin ASC"; + + Uri.Builder builder = Instances.CONTENT_BY_DAY_URI.buildUpon(); + ContentUris.appendId(builder, startDay); + ContentUris.appendId(builder, endDay); + if (TextUtils.isEmpty(selection)) { + selection = WHERE_CALENDARS_SELECTED; + selectionArgs = WHERE_CALENDARS_ARGS; + } else { + selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED; + if (selectionArgs != null && selectionArgs.length > 0) { + selectionArgs = Arrays.copyOf(selectionArgs, selectionArgs.length + 1); + selectionArgs[selectionArgs.length - 1] = WHERE_CALENDARS_ARGS[0]; + } else { + selectionArgs = WHERE_CALENDARS_ARGS; + } + } + return cr.query(builder.build(), projection, selection, selectionArgs, + orderBy == null ? DEFAULT_SORT_ORDER : orderBy); + } + + /** + * Adds all the events from the cursors to the events list. + * + * @param events The list of events + * @param cEvents Events to add to the list + * @param context + * @param startDay + * @param endDay + */ + public static void buildEventsFromCursor( + ArrayList events, Cursor cEvents, Context context, int startDay, int endDay) { + if (cEvents == null || events == null) { + Log.e(TAG, "buildEventsFromCursor: null cursor or null events list!"); + return; + } + + int count = cEvents.getCount(); + + if (count == 0) { + return; + } + + Resources res = context.getResources(); + mNoTitleString = res.getString(R.string.no_title_label); + mNoColorColor = res.getColor(R.color.event_center); + // Sort events in two passes so we ensure the allday and standard events + // get sorted in the correct order + cEvents.moveToPosition(-1); + while (cEvents.moveToNext()) { + Event e = generateEventFromCursor(cEvents); + if (e.startDay > endDay || e.endDay < startDay) { + continue; + } + events.add(e); + } + } + + /** + * @param cEvents Cursor pointing at event + * @return An event created from the cursor + */ + private static Event generateEventFromCursor(Cursor cEvents) { + Event e = new Event(); + + e.id = cEvents.getLong(PROJECTION_EVENT_ID_INDEX); + e.title = cEvents.getString(PROJECTION_TITLE_INDEX); + e.location = cEvents.getString(PROJECTION_LOCATION_INDEX); + e.allDay = cEvents.getInt(PROJECTION_ALL_DAY_INDEX) != 0; + e.organizer = cEvents.getString(PROJECTION_ORGANIZER_INDEX); + e.guestsCanModify = cEvents.getInt(PROJECTION_GUESTS_CAN_INVITE_OTHERS_INDEX) != 0; + + if (e.title == null || e.title.length() == 0) { + e.title = mNoTitleString; + } + + if (!cEvents.isNull(PROJECTION_COLOR_INDEX)) { + // Read the color from the database + e.color = Utils.getDisplayColorFromColor(cEvents.getInt(PROJECTION_COLOR_INDEX)); + } else { + e.color = mNoColorColor; + } + + long eStart = cEvents.getLong(PROJECTION_BEGIN_INDEX); + long eEnd = cEvents.getLong(PROJECTION_END_INDEX); + + e.startMillis = eStart; + e.startTime = cEvents.getInt(PROJECTION_START_MINUTE_INDEX); + e.startDay = cEvents.getInt(PROJECTION_START_DAY_INDEX); + + e.endMillis = eEnd; + e.endTime = cEvents.getInt(PROJECTION_END_MINUTE_INDEX); + e.endDay = cEvents.getInt(PROJECTION_END_DAY_INDEX); + + e.hasAlarm = cEvents.getInt(PROJECTION_HAS_ALARM_INDEX) != 0; + + // Check if this is a repeating event + String rrule = cEvents.getString(PROJECTION_RRULE_INDEX); + String rdate = cEvents.getString(PROJECTION_RDATE_INDEX); + if (!TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate)) { + e.isRepeating = true; + } else { + e.isRepeating = false; + } + + e.selfAttendeeStatus = cEvents.getInt(PROJECTION_SELF_ATTENDEE_STATUS_INDEX); + return e; + } + + /** + * Computes a position for each event. Each event is displayed + * as a non-overlapping rectangle. For normal events, these rectangles + * are displayed in separate columns in the week view and day view. For + * all-day events, these rectangles are displayed in separate rows along + * the top. In both cases, each event is assigned two numbers: N, and + * Max, that specify that this event is the Nth event of Max number of + * events that are displayed in a group. The width and position of each + * rectangle depend on the maximum number of rectangles that occur at + * the same time. + * + * @param eventsList the list of events, sorted into increasing time order + * @param minimumDurationMillis minimum duration acceptable as cell height of each event + * rectangle in millisecond. Should be 0 when it is not determined. + */ + /* package */ static void computePositions(ArrayList eventsList, + long minimumDurationMillis) { + if (eventsList == null) { + return; + } + + // Compute the column positions separately for the all-day events + doComputePositions(eventsList, minimumDurationMillis, false); + doComputePositions(eventsList, minimumDurationMillis, true); + } + + private static void doComputePositions(ArrayList eventsList, + long minimumDurationMillis, boolean doAlldayEvents) { + final ArrayList activeList = new ArrayList(); + final ArrayList groupList = new ArrayList(); + + if (minimumDurationMillis < 0) { + minimumDurationMillis = 0; + } + + long colMask = 0; + int maxCols = 0; + for (Event event : eventsList) { + // Process all-day events separately + if (event.drawAsAllday() != doAlldayEvents) + continue; + + if (!doAlldayEvents) { + colMask = removeNonAlldayActiveEvents( + event, activeList.iterator(), minimumDurationMillis, colMask); + } else { + colMask = removeAlldayActiveEvents(event, activeList.iterator(), colMask); + } + + // If the active list is empty, then reset the max columns, clear + // the column bit mask, and empty the groupList. + if (activeList.isEmpty()) { + for (Event ev : groupList) { + ev.setMaxColumns(maxCols); + } + maxCols = 0; + colMask = 0; + groupList.clear(); + } + + // Find the first empty column. Empty columns are represented by + // zero bits in the column mask "colMask". + int col = findFirstZeroBit(colMask); + if (col == 64) + col = 63; + colMask |= (1L << col); + event.setColumn(col); + activeList.add(event); + groupList.add(event); + int len = activeList.size(); + if (maxCols < len) + maxCols = len; + } + for (Event ev : groupList) { + ev.setMaxColumns(maxCols); + } + } + + private static long removeAlldayActiveEvents(Event event, Iterator iter, long colMask) { + // Remove the inactive allday events. An event on the active list + // becomes inactive when the end day is less than the current event's + // start day. + while (iter.hasNext()) { + final Event active = iter.next(); + if (active.endDay < event.startDay) { + colMask &= ~(1L << active.getColumn()); + iter.remove(); + } + } + return colMask; + } + + private static long removeNonAlldayActiveEvents( + Event event, Iterator iter, long minDurationMillis, long colMask) { + long start = event.getStartMillis(); + // Remove the inactive events. An event on the active list + // becomes inactive when its end time is less than or equal to + // the current event's start time. + while (iter.hasNext()) { + final Event active = iter.next(); + + final long duration = Math.max( + active.getEndMillis() - active.getStartMillis(), minDurationMillis); + if ((active.getStartMillis() + duration) <= start) { + colMask &= ~(1L << active.getColumn()); + iter.remove(); + } + } + return colMask; + } + + public static int findFirstZeroBit(long val) { + for (int ii = 0; ii < 64; ++ii) { + if ((val & (1L << ii)) == 0) + return ii; + } + return 64; + } + + public final void dump() { + Log.e("Cal", "+-----------------------------------------+"); + Log.e("Cal", "+ id = " + id); + Log.e("Cal", "+ color = " + color); + Log.e("Cal", "+ title = " + title); + Log.e("Cal", "+ location = " + location); + Log.e("Cal", "+ allDay = " + allDay); + Log.e("Cal", "+ startDay = " + startDay); + 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, + int endMinute) { + if (endDay < julianDay) { + return false; + } + + if (startDay > julianDay) { + return false; + } + + if (endDay == julianDay) { + if (endTime < startMinute) { + return false; + } + // An event that ends at the start minute should not be considered + // as intersecting the given time span, but don't exclude + // zero-length (or very short) events. + if (endTime == startMinute + && (startTime != endTime || startDay != endDay)) { + return false; + } + } + + if (startDay == julianDay && startTime > endMinute) { + return false; + } + + return true; + } + + /** + * Returns the event title and location separated by a comma. If the + * location is already part of the title (at the end of the title), then + * just the title is returned. + * + * @return the event title and location as a String + */ + public String getTitleAndLocation() { + String text = title.toString(); + + // Append the location to the title, unless the title ends with the + // location (for example, "meeting in building 42" ends with the + // location). + if (location != null) { + String locationString = location.toString(); + if (!text.endsWith(locationString)) { + text += ", " + locationString; + } + } + return text; + } + + public void setColumn(int column) { + mColumn = column; + } + + public int getColumn() { + return mColumn; + } + + public void setMaxColumns(int maxColumns) { + mMaxColumns = maxColumns; + } + + public int getMaxColumns() { + return mMaxColumns; + } + + public void setStartMillis(long startMillis) { + this.startMillis = startMillis; + } + + public long getStartMillis() { + return startMillis; + } + + public void setEndMillis(long endMillis) { + this.endMillis = endMillis; + } + + public long getEndMillis() { + return endMillis; + } + + public boolean drawAsAllday() { + // Use >= so we'll pick up Exchange allday events + return allDay || endMillis - startMillis >= DateUtils.DAY_IN_MILLIS; + } +} -- cgit v1.2.3 From eef428e666bf455e9c902b9023c12369e658c1f9 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Tue, 22 Jun 2021 16:55:49 +0000 Subject: AOSP/Calendar - Event fully converted with bp file Using Android Studio, Event.java has been fully converted to Kotlin. The converter did a great job at organizing the static members of the Event class into a "companion object" although annotations such as @JvmStatic and @JvmField had to be manually added for static methods and instance field respectively. Another interesting issue had to do with access to Event fields from other classes. Since getters and setters are optional in Kotlin while maintaining the same level of encapsulation, the converter did not include them. This caused other unconverted classes that depended on the Event class to now have "unresolved references" for methods such as "getMaxColumns()". These had to replaced as if the variable was directly accessing the property, such as "variable.maxColumns". This is a trend in Kotlin that also be seen with .size() vs .size in which the properties that are returned by Java methods can be directly accessed in Kotlin. DayView.java has failed build tests on two targets because of the lack of ".getColumn()". Once fully converted, DayView.kt shouldn't have to rely on a getter but for now, a getter has been included. Another interesting problem was that without adding @JvmField besides public instance fields causes the compiler to view that field as unaccessible. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.154 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 6m 23s Total aggregated tests run time: 6m 23s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.98 tests/sec [376 tests / 383671 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 7497 ms || clean = 2141 ms Total preparation time: 7s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 7m 52s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: I1a99764c3abb6626706675efabc9b2bdbfac59f2 --- Android.bp | 1 + src/com/android/calendar/Event.kt | 1100 ++++++++++---------- src/com/android/calendar/EventGeometry.kt | 4 +- src/com/android/calendar/EventLoader.kt | 55 +- src/com/android/calendar/GeneralPreferences.kt | 25 +- .../calendar/alerts/QuickResponseActivity.kt | 4 +- 6 files changed, 602 insertions(+), 587 deletions(-) diff --git a/Android.bp b/Android.bp index d491ba87..57cbfb93 100644 --- a/Android.bp +++ b/Android.bp @@ -29,6 +29,7 @@ exclude_srcsd = [ "src/**/calendar/alerts/QuickResponseActivity.java", "src/**/calendar/CalendarController.java", "src/**/calendar/DayOfMonthDrawable.java", + "src/**/calendar/Event.java", "src/**/calendar/EventInfoActivity.java", "src/**/calendar/StickyHeaderListView.java", ] diff --git a/src/com/android/calendar/Event.kt b/src/com/android/calendar/Event.kt index 095e43e7..c21a0a0e 100644 --- a/src/com/android/calendar/Event.kt +++ b/src/com/android/calendar/Event.kt @@ -1,11 +1,11 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2021 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 + * 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, @@ -13,572 +13,579 @@ * 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.ContentUris; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.os.Debug; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.provider.CalendarContract.Instances; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.concurrent.atomic.AtomicInteger; +package com.android.calendar + +import android.content.ContentResolver +import android.content.ContentUris +import android.content.Context +import android.content.SharedPreferences +import android.content.res.Resources +import android.database.Cursor +import android.net.Uri +import android.os.Debug +import android.provider.CalendarContract.Attendees +import android.provider.CalendarContract.Calendars +import android.provider.CalendarContract.Events +import android.provider.CalendarContract.Instances +import android.text.TextUtils +import android.text.format.DateUtils +import android.util.Log + +import java.util.ArrayList +import java.util.Arrays +import java.util.Iterator +import java.util.concurrent.atomic.AtomicInteger // TODO: should Event be Parcelable so it can be passed via Intents? -public class Event implements Cloneable { - - private static final String TAG = "CalEvent"; - private static final boolean PROFILE = false; - - /** - * The sort order is: - * 1) events with an earlier start (begin for normal events, startday for allday) - * 2) events with a later end (end for normal events, endday for allday) - * 3) the title (unnecessary, but nice) - * - * The start and end day is sorted first so that all day events are - * sorted correctly with respect to events that are >24 hours (and - * therefore show up in the allday area). - */ - private static final String SORT_EVENTS_BY = - "begin ASC, end DESC, title ASC"; - private static final String SORT_ALLDAY_BY = - "startDay ASC, endDay DESC, title ASC"; - private static final String DISPLAY_AS_ALLDAY = "dispAllday"; - - private static final String EVENTS_WHERE = DISPLAY_AS_ALLDAY + "=0"; - private static final String ALLDAY_WHERE = DISPLAY_AS_ALLDAY + "=1"; - - // The projection to use when querying instances to build a list of events - public static final String[] EVENT_PROJECTION = new String[] { - Instances.TITLE, // 0 - Instances.EVENT_LOCATION, // 1 - Instances.ALL_DAY, // 2 - Instances.DISPLAY_COLOR, // 3 If SDK < 16, set to Instances.CALENDAR_COLOR. - Instances.EVENT_TIMEZONE, // 4 - Instances.EVENT_ID, // 5 - Instances.BEGIN, // 6 - Instances.END, // 7 - Instances._ID, // 8 - Instances.START_DAY, // 9 - Instances.END_DAY, // 10 - Instances.START_MINUTE, // 11 - Instances.END_MINUTE, // 12 - Instances.HAS_ALARM, // 13 - Instances.RRULE, // 14 - Instances.RDATE, // 15 - Instances.SELF_ATTENDEE_STATUS, // 16 - Events.ORGANIZER, // 17 - Events.GUESTS_CAN_MODIFY, // 18 - Instances.ALL_DAY + "=1 OR (" + Instances.END + "-" + Instances.BEGIN + ")>=" - + DateUtils.DAY_IN_MILLIS + " AS " + DISPLAY_AS_ALLDAY, // 19 - }; - - // The indices for the projection array above. - private static final int PROJECTION_TITLE_INDEX = 0; - private static final int PROJECTION_LOCATION_INDEX = 1; - private static final int PROJECTION_ALL_DAY_INDEX = 2; - private static final int PROJECTION_COLOR_INDEX = 3; - private static final int PROJECTION_TIMEZONE_INDEX = 4; - private static final int PROJECTION_EVENT_ID_INDEX = 5; - private static final int PROJECTION_BEGIN_INDEX = 6; - private static final int PROJECTION_END_INDEX = 7; - private static final int PROJECTION_START_DAY_INDEX = 9; - private static final int PROJECTION_END_DAY_INDEX = 10; - private static final int PROJECTION_START_MINUTE_INDEX = 11; - private static final int PROJECTION_END_MINUTE_INDEX = 12; - private static final int PROJECTION_HAS_ALARM_INDEX = 13; - 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; - private static final int PROJECTION_DISPLAY_AS_ALLDAY = 19; - - static { - if (!Utils.isJellybeanOrLater()) { - EVENT_PROJECTION[PROJECTION_COLOR_INDEX] = Instances.CALENDAR_COLOR; - } - } - - private static String mNoTitleString; - private static int mNoColorColor; - - 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 - public int startTime; // Start and end time are in minutes since midnight - public int endTime; - - public long startMillis; // UTC milliseconds since the epoch - public long endMillis; // UTC milliseconds since the epoch - private int mColumn; - private int mMaxColumns; - - public boolean hasAlarm; - public boolean isRepeating; - - public int selfAttendeeStatus; - - // The coordinates of the event rectangle drawn on the screen. - public float left; - public float right; - public float top; - public float bottom; - - // These 4 fields are used for navigating among events within the selected - // hour in the Day and Week view. - public Event nextRight; - public Event nextLeft; - public Event nextUp; - public Event nextDown; - - @Override - public final Object clone() throws CloneNotSupportedException { - super.clone(); - Event e = new Event(); - - e.title = title; - e.color = color; - e.location = location; - e.allDay = allDay; - e.startDay = startDay; - e.endDay = endDay; - e.startTime = startTime; - e.endTime = endTime; - e.startMillis = startMillis; - e.endMillis = endMillis; - e.hasAlarm = hasAlarm; - e.isRepeating = isRepeating; - e.selfAttendeeStatus = selfAttendeeStatus; - e.organizer = organizer; - e.guestsCanModify = guestsCanModify; - - return e; - } - - public final void copyTo(Event dest) { - dest.id = id; - dest.title = title; - dest.color = color; - dest.location = location; - dest.allDay = allDay; - dest.startDay = startDay; - dest.endDay = endDay; - dest.startTime = startTime; - dest.endTime = endTime; - dest.startMillis = startMillis; - dest.endMillis = endMillis; - dest.hasAlarm = hasAlarm; - dest.isRepeating = isRepeating; - dest.selfAttendeeStatus = selfAttendeeStatus; - dest.organizer = organizer; - dest.guestsCanModify = guestsCanModify; - } - - public static final Event newInstance() { - Event e = new Event(); - - e.id = 0; - e.title = null; - e.color = 0; - e.location = null; - e.allDay = false; - e.startDay = 0; - e.endDay = 0; - e.startTime = 0; - e.endTime = 0; - e.startMillis = 0; - e.endMillis = 0; - e.hasAlarm = false; - e.isRepeating = false; - e.selfAttendeeStatus = Attendees.ATTENDEE_STATUS_NONE; - - return e; - } - - /** - * Loads days days worth of instances starting at startDay. - */ - public static void loadEvents(Context context, ArrayList events, int startDay, int days, - int requestId, AtomicInteger sequenceNumber) { - - if (PROFILE) { - Debug.startMethodTracing("loadEvents"); +class Event : Cloneable { + companion object { + private const val TAG = "CalEvent" + private const val PROFILE = false + + /** + * The sort order is: + * 1) events with an earlier start (begin for normal events, startday for allday) + * 2) events with a later end (end for normal events, endday for allday) + * 3) the title (unnecessary, but nice) + * + * The start and end day is sorted first so that all day events are + * sorted correctly with respect to events that are >24 hours (and + * therefore show up in the allday area). + */ + private const val SORT_EVENTS_BY = "begin ASC, end DESC, title ASC" + private const val SORT_ALLDAY_BY = "startDay ASC, endDay DESC, title ASC" + private const val DISPLAY_AS_ALLDAY = "dispAllday" + private const val EVENTS_WHERE = DISPLAY_AS_ALLDAY + "=0" + private const val ALLDAY_WHERE = DISPLAY_AS_ALLDAY + "=1" + + // The projection to use when querying instances to build a list of events + @JvmField + val EVENT_PROJECTION = arrayOf( + Instances.TITLE, // 0 + Instances.EVENT_LOCATION, // 1 + Instances.ALL_DAY, // 2 + Instances.DISPLAY_COLOR, // 3 If SDK < 16, set to Instances.CALENDAR_COLOR. + Instances.EVENT_TIMEZONE, // 4 + Instances.EVENT_ID, // 5 + Instances.BEGIN, // 6 + Instances.END, // 7 + Instances._ID, // 8 + Instances.START_DAY, // 9 + Instances.END_DAY, // 10 + Instances.START_MINUTE, // 11 + Instances.END_MINUTE, // 12 + Instances.HAS_ALARM, // 13 + Instances.RRULE, // 14 + Instances.RDATE, // 15 + Instances.SELF_ATTENDEE_STATUS, // 16 + Events.ORGANIZER, // 17 + Events.GUESTS_CAN_MODIFY, // 18 + Instances.ALL_DAY.toString() + "=1 OR (" + Instances.END + "-" + + Instances.BEGIN + ")>=" + + DateUtils.DAY_IN_MILLIS + " AS " + DISPLAY_AS_ALLDAY + ) + + // The indices for the projection array above. + private const val PROJECTION_TITLE_INDEX = 0 + private const val PROJECTION_LOCATION_INDEX = 1 + private const val PROJECTION_ALL_DAY_INDEX = 2 + private const val PROJECTION_COLOR_INDEX = 3 + private const val PROJECTION_TIMEZONE_INDEX = 4 + private const val PROJECTION_EVENT_ID_INDEX = 5 + private const val PROJECTION_BEGIN_INDEX = 6 + private const val PROJECTION_END_INDEX = 7 + private const val PROJECTION_START_DAY_INDEX = 9 + private const val PROJECTION_END_DAY_INDEX = 10 + private const val PROJECTION_START_MINUTE_INDEX = 11 + private const val PROJECTION_END_MINUTE_INDEX = 12 + private const val PROJECTION_HAS_ALARM_INDEX = 13 + private const val PROJECTION_RRULE_INDEX = 14 + private const val PROJECTION_RDATE_INDEX = 15 + private const val PROJECTION_SELF_ATTENDEE_STATUS_INDEX = 16 + private const val PROJECTION_ORGANIZER_INDEX = 17 + private const val PROJECTION_GUESTS_CAN_INVITE_OTHERS_INDEX = 18 + private const val PROJECTION_DISPLAY_AS_ALLDAY = 19 + private var mNoTitleString: String? = null + private var mNoColorColor = 0 + @JvmStatic fun newInstance(): Event { + val e = Event() + e.id = 0 + e.title = null + e.color = 0 + e.location = null + e.allDay = false + e.startDay = 0 + e.endDay = 0 + e.startTime = 0 + e.endTime = 0 + e.startMillis = 0 + e.endMillis = 0 + e.hasAlarm = false + e.isRepeating = false + e.selfAttendeeStatus = Attendees.ATTENDEE_STATUS_NONE + return e } - Cursor cEvents = null; - Cursor cAllday = null; - - events.clear(); - try { - int endDay = startDay + days - 1; - - // We use the byDay instances query to get a list of all events for - // the days we're interested in. - // The sort order is: events with an earlier start time occur - // first and if the start times are the same, then events with - // a later end time occur first. The later end time is ordered - // first so that long rectangles in the calendar views appear on - // the left side. If the start and end times of two events are - // the same then we sort alphabetically on the title. This isn't - // required for correctness, it just adds a nice touch. - - // Respect the preference to show/hide declined events - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - boolean hideDeclined = prefs.getBoolean(GeneralPreferences.KEY_HIDE_DECLINED, - false); - - String where = EVENTS_WHERE; - String whereAllday = ALLDAY_WHERE; - if (hideDeclined) { - String hideString = " AND " + Instances.SELF_ATTENDEE_STATUS + "!=" - + Attendees.ATTENDEE_STATUS_DECLINED; - where += hideString; - whereAllday += hideString; + /** + * Loads *days* days worth of instances starting at *startDay*. + */ + @JvmStatic fun loadEvents( + context: Context?, + events: ArrayList, + startDay: Int, + days: Int, + requestId: Int, + sequenceNumber: AtomicInteger? + ) { + if (PROFILE) { + Debug.startMethodTracing("loadEvents") } - - cEvents = instancesQuery(context.getContentResolver(), EVENT_PROJECTION, startDay, - endDay, where, null, SORT_EVENTS_BY); - cAllday = instancesQuery(context.getContentResolver(), EVENT_PROJECTION, startDay, - endDay, whereAllday, null, SORT_ALLDAY_BY); - - // Check if we should return early because there are more recent - // load requests waiting. - if (requestId != sequenceNumber.get()) { - return; + var cEvents: Cursor? = null + var cAllday: Cursor? = null + events.clear() + try { + val endDay = startDay + days - 1 + + // We use the byDay instances query to get a list of all events for + // the days we're interested in. + // The sort order is: events with an earlier start time occur + // first and if the start times are the same, then events with + // a later end time occur first. The later end time is ordered + // first so that long rectangles in the calendar views appear on + // the left side. If the start and end times of two events are + // the same then we sort alphabetically on the title. This isn't + // required for correctness, it just adds a nice touch. + + // Respect the preference to show/hide declined events + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + val hideDeclined: Boolean = prefs?.getBoolean( + GeneralPreferences.KEY_HIDE_DECLINED, + false + ) as Boolean + var where = EVENTS_WHERE + var whereAllday = ALLDAY_WHERE + if (hideDeclined) { + val hideString = (" AND " + Instances.SELF_ATTENDEE_STATUS.toString() + "!=" + + Attendees.ATTENDEE_STATUS_DECLINED) + where += hideString + whereAllday += hideString + } + cEvents = instancesQuery( + context?.getContentResolver(), EVENT_PROJECTION, startDay, + endDay, where, null, SORT_EVENTS_BY + ) + cAllday = instancesQuery( + context?.getContentResolver(), EVENT_PROJECTION, startDay, + endDay, whereAllday, null, SORT_ALLDAY_BY + ) + + // Check if we should return early because there are more recent + // load requests waiting. + if (requestId != sequenceNumber?.get()) { + return + } + buildEventsFromCursor(events, cEvents, context, startDay, endDay) + buildEventsFromCursor(events, cAllday, context, startDay, endDay) + } finally { + if (cEvents != null) { + cEvents.close() + } + if (cAllday != null) { + cAllday.close() + } + if (PROFILE) { + Debug.stopMethodTracing() + } } + } - buildEventsFromCursor(events, cEvents, context, startDay, endDay); - buildEventsFromCursor(events, cAllday, context, startDay, endDay); + /** + * Performs a query to return all visible instances in the given range + * that match the given selection. This is a blocking function and + * should not be done on the UI thread. This will cause an expansion of + * recurring events to fill this time range if they are not already + * expanded and will slow down for larger time ranges with many + * recurring events. + * + * @param cr The ContentResolver to use for the query + * @param projection The columns to return + * @param begin The start of the time range to query in UTC millis since + * epoch + * @param end The end of the time range to query in UTC millis since + * epoch + * @param selection Filter on the query as an SQL WHERE statement + * @param selectionArgs Args to replace any '?'s in the selection + * @param orderBy How to order the rows as an SQL ORDER BY statement + * @return A Cursor of instances matching the selection + */ + @JvmStatic private fun instancesQuery( + cr: ContentResolver?, + projection: Array, + startDay: Int, + endDay: Int, + selection: String, + selectionArgs: Array?, + orderBy: String? + ): Cursor? { + var selection = selection + var selectionArgs = selectionArgs + val WHERE_CALENDARS_SELECTED: String = Calendars.VISIBLE.toString() + "=?" + val WHERE_CALENDARS_ARGS = arrayOf("1") + val DEFAULT_SORT_ORDER = "begin ASC" + val builder: Uri.Builder = Instances.CONTENT_BY_DAY_URI.buildUpon() + ContentUris.appendId(builder, startDay.toLong()) + ContentUris.appendId(builder, endDay.toLong()) + if (TextUtils.isEmpty(selection)) { + selection = WHERE_CALENDARS_SELECTED + selectionArgs = WHERE_CALENDARS_ARGS + } else { + selection = "($selection) AND $WHERE_CALENDARS_SELECTED" + if (selectionArgs != null && selectionArgs.size > 0) { + selectionArgs = Arrays.copyOf(selectionArgs, selectionArgs.size + 1) + selectionArgs[selectionArgs.size - 1] = WHERE_CALENDARS_ARGS[0] + } else { + selectionArgs = WHERE_CALENDARS_ARGS + } + } + return cr?.query( + builder.build(), projection, selection, selectionArgs, + orderBy ?: DEFAULT_SORT_ORDER + ) + } - } finally { - if (cEvents != null) { - cEvents.close(); + /** + * Adds all the events from the cursors to the events list. + * + * @param events The list of events + * @param cEvents Events to add to the list + * @param context + * @param startDay + * @param endDay + */ + @JvmStatic fun buildEventsFromCursor( + events: ArrayList?, + cEvents: Cursor?, + context: Context?, + startDay: Int, + endDay: Int + ) { + if (cEvents == null || events == null) { + Log.e(TAG, "buildEventsFromCursor: null cursor or null events list!") + return } - if (cAllday != null) { - cAllday.close(); + val count: Int = cEvents.getCount() + if (count == 0) { + return } - if (PROFILE) { - Debug.stopMethodTracing(); + val res: Resources? = context?.getResources() + mNoTitleString = res?.getString(R.string.no_title_label) + mNoColorColor = res?.getColor(R.color.event_center) as Int + // Sort events in two passes so we ensure the allday and standard events + // get sorted in the correct order + cEvents.moveToPosition(-1) + while (cEvents.moveToNext()) { + val e = generateEventFromCursor(cEvents) + if (e.startDay > endDay || e.endDay < startDay) { + continue + } + events.add(e) } } - } - /** - * Performs a query to return all visible instances in the given range - * that match the given selection. This is a blocking function and - * should not be done on the UI thread. This will cause an expansion of - * recurring events to fill this time range if they are not already - * expanded and will slow down for larger time ranges with many - * recurring events. - * - * @param cr The ContentResolver to use for the query - * @param projection The columns to return - * @param begin The start of the time range to query in UTC millis since - * epoch - * @param end The end of the time range to query in UTC millis since - * epoch - * @param selection Filter on the query as an SQL WHERE statement - * @param selectionArgs Args to replace any '?'s in the selection - * @param orderBy How to order the rows as an SQL ORDER BY statement - * @return A Cursor of instances matching the selection - */ - private static final Cursor instancesQuery(ContentResolver cr, String[] projection, - int startDay, int endDay, String selection, String[] selectionArgs, String orderBy) { - String WHERE_CALENDARS_SELECTED = Calendars.VISIBLE + "=?"; - String[] WHERE_CALENDARS_ARGS = {"1"}; - String DEFAULT_SORT_ORDER = "begin ASC"; - - Uri.Builder builder = Instances.CONTENT_BY_DAY_URI.buildUpon(); - ContentUris.appendId(builder, startDay); - ContentUris.appendId(builder, endDay); - if (TextUtils.isEmpty(selection)) { - selection = WHERE_CALENDARS_SELECTED; - selectionArgs = WHERE_CALENDARS_ARGS; - } else { - selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED; - if (selectionArgs != null && selectionArgs.length > 0) { - selectionArgs = Arrays.copyOf(selectionArgs, selectionArgs.length + 1); - selectionArgs[selectionArgs.length - 1] = WHERE_CALENDARS_ARGS[0]; + /** + * @param cEvents Cursor pointing at event + * @return An event created from the cursor + */ + @JvmStatic private fun generateEventFromCursor(cEvents: Cursor): Event { + val e = Event() + e.id = cEvents.getLong(PROJECTION_EVENT_ID_INDEX) + e.title = cEvents.getString(PROJECTION_TITLE_INDEX) + e.location = cEvents.getString(PROJECTION_LOCATION_INDEX) + e.allDay = cEvents.getInt(PROJECTION_ALL_DAY_INDEX) !== 0 + e.organizer = cEvents.getString(PROJECTION_ORGANIZER_INDEX) + e.guestsCanModify = cEvents.getInt(PROJECTION_GUESTS_CAN_INVITE_OTHERS_INDEX) !== 0 + if (e.title == null || e.title!!.length == 0) { + e.title = mNoTitleString + } + if (!cEvents.isNull(PROJECTION_COLOR_INDEX)) { + // Read the color from the database + e.color = Utils.getDisplayColorFromColor(cEvents.getInt(PROJECTION_COLOR_INDEX)) } else { - selectionArgs = WHERE_CALENDARS_ARGS; + e.color = mNoColorColor } - } - return cr.query(builder.build(), projection, selection, selectionArgs, - orderBy == null ? DEFAULT_SORT_ORDER : orderBy); - } - - /** - * Adds all the events from the cursors to the events list. - * - * @param events The list of events - * @param cEvents Events to add to the list - * @param context - * @param startDay - * @param endDay - */ - public static void buildEventsFromCursor( - ArrayList events, Cursor cEvents, Context context, int startDay, int endDay) { - if (cEvents == null || events == null) { - Log.e(TAG, "buildEventsFromCursor: null cursor or null events list!"); - return; - } - - int count = cEvents.getCount(); - - if (count == 0) { - return; - } - - Resources res = context.getResources(); - mNoTitleString = res.getString(R.string.no_title_label); - mNoColorColor = res.getColor(R.color.event_center); - // Sort events in two passes so we ensure the allday and standard events - // get sorted in the correct order - cEvents.moveToPosition(-1); - while (cEvents.moveToNext()) { - Event e = generateEventFromCursor(cEvents); - if (e.startDay > endDay || e.endDay < startDay) { - continue; + val eStart: Long = cEvents.getLong(PROJECTION_BEGIN_INDEX) + val eEnd: Long = cEvents.getLong(PROJECTION_END_INDEX) + e.startMillis = eStart + e.startTime = cEvents.getInt(PROJECTION_START_MINUTE_INDEX) + e.startDay = cEvents.getInt(PROJECTION_START_DAY_INDEX) + e.endMillis = eEnd + e.endTime = cEvents.getInt(PROJECTION_END_MINUTE_INDEX) + e.endDay = cEvents.getInt(PROJECTION_END_DAY_INDEX) + e.hasAlarm = cEvents.getInt(PROJECTION_HAS_ALARM_INDEX) !== 0 + + // Check if this is a repeating event + val rrule: String = cEvents.getString(PROJECTION_RRULE_INDEX) + val rdate: String = cEvents.getString(PROJECTION_RDATE_INDEX) + if (!TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate)) { + e.isRepeating = true + } else { + e.isRepeating = false } - events.add(e); + e.selfAttendeeStatus = cEvents.getInt(PROJECTION_SELF_ATTENDEE_STATUS_INDEX) + return e } - } - - /** - * @param cEvents Cursor pointing at event - * @return An event created from the cursor - */ - private static Event generateEventFromCursor(Cursor cEvents) { - Event e = new Event(); - - e.id = cEvents.getLong(PROJECTION_EVENT_ID_INDEX); - e.title = cEvents.getString(PROJECTION_TITLE_INDEX); - e.location = cEvents.getString(PROJECTION_LOCATION_INDEX); - e.allDay = cEvents.getInt(PROJECTION_ALL_DAY_INDEX) != 0; - e.organizer = cEvents.getString(PROJECTION_ORGANIZER_INDEX); - e.guestsCanModify = cEvents.getInt(PROJECTION_GUESTS_CAN_INVITE_OTHERS_INDEX) != 0; - - if (e.title == null || e.title.length() == 0) { - e.title = mNoTitleString; - } - - if (!cEvents.isNull(PROJECTION_COLOR_INDEX)) { - // Read the color from the database - e.color = Utils.getDisplayColorFromColor(cEvents.getInt(PROJECTION_COLOR_INDEX)); - } else { - e.color = mNoColorColor; - } - - long eStart = cEvents.getLong(PROJECTION_BEGIN_INDEX); - long eEnd = cEvents.getLong(PROJECTION_END_INDEX); - - e.startMillis = eStart; - e.startTime = cEvents.getInt(PROJECTION_START_MINUTE_INDEX); - e.startDay = cEvents.getInt(PROJECTION_START_DAY_INDEX); - - e.endMillis = eEnd; - e.endTime = cEvents.getInt(PROJECTION_END_MINUTE_INDEX); - e.endDay = cEvents.getInt(PROJECTION_END_DAY_INDEX); - - e.hasAlarm = cEvents.getInt(PROJECTION_HAS_ALARM_INDEX) != 0; - // Check if this is a repeating event - String rrule = cEvents.getString(PROJECTION_RRULE_INDEX); - String rdate = cEvents.getString(PROJECTION_RDATE_INDEX); - if (!TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate)) { - e.isRepeating = true; - } else { - e.isRepeating = false; - } - - e.selfAttendeeStatus = cEvents.getInt(PROJECTION_SELF_ATTENDEE_STATUS_INDEX); - return e; - } + /** + * Computes a position for each event. Each event is displayed + * as a non-overlapping rectangle. For normal events, these rectangles + * are displayed in separate columns in the week view and day view. For + * all-day events, these rectangles are displayed in separate rows along + * the top. In both cases, each event is assigned two numbers: N, and + * Max, that specify that this event is the Nth event of Max number of + * events that are displayed in a group. The width and position of each + * rectangle depend on the maximum number of rectangles that occur at + * the same time. + * + * @param eventsList the list of events, sorted into increasing time order + * @param minimumDurationMillis minimum duration acceptable as cell height of each event + * rectangle in millisecond. Should be 0 when it is not determined. + */ + /* package */ + @JvmStatic fun computePositions( + eventsList: ArrayList?, + minimumDurationMillis: Long + ) { + if (eventsList == null) { + return + } - /** - * Computes a position for each event. Each event is displayed - * as a non-overlapping rectangle. For normal events, these rectangles - * are displayed in separate columns in the week view and day view. For - * all-day events, these rectangles are displayed in separate rows along - * the top. In both cases, each event is assigned two numbers: N, and - * Max, that specify that this event is the Nth event of Max number of - * events that are displayed in a group. The width and position of each - * rectangle depend on the maximum number of rectangles that occur at - * the same time. - * - * @param eventsList the list of events, sorted into increasing time order - * @param minimumDurationMillis minimum duration acceptable as cell height of each event - * rectangle in millisecond. Should be 0 when it is not determined. - */ - /* package */ static void computePositions(ArrayList eventsList, - long minimumDurationMillis) { - if (eventsList == null) { - return; + // Compute the column positions separately for the all-day events + doComputePositions(eventsList, minimumDurationMillis, false) + doComputePositions(eventsList, minimumDurationMillis, true) } - // Compute the column positions separately for the all-day events - doComputePositions(eventsList, minimumDurationMillis, false); - doComputePositions(eventsList, minimumDurationMillis, true); - } + @JvmStatic private fun doComputePositions( + eventsList: ArrayList, + minimumDurationMillis: Long, + doAlldayEvents: Boolean + ) { + var minimumDurationMillis = minimumDurationMillis + val activeList: ArrayList = ArrayList() + val groupList: ArrayList = ArrayList() + if (minimumDurationMillis < 0) { + minimumDurationMillis = 0 + } + var colMask: Long = 0 + var maxCols = 0 + for (event in eventsList) { + // Process all-day events separately + if (event.drawAsAllday() != doAlldayEvents) continue + colMask = if (!doAlldayEvents) { + removeNonAlldayActiveEvents( + event, activeList.iterator() as Iterator, + minimumDurationMillis, colMask + ) + } else { + removeAlldayActiveEvents(event, activeList.iterator() + as Iterator, colMask) + } - private static void doComputePositions(ArrayList eventsList, - long minimumDurationMillis, boolean doAlldayEvents) { - final ArrayList activeList = new ArrayList(); - final ArrayList groupList = new ArrayList(); + // If the active list is empty, then reset the max columns, clear + // the column bit mask, and empty the groupList. + if (activeList.isEmpty()) { + for (ev in groupList) { + ev.maxColumns = maxCols + } + maxCols = 0 + colMask = 0 + groupList.clear() + } - if (minimumDurationMillis < 0) { - minimumDurationMillis = 0; + // Find the first empty column. Empty columns are represented by + // zero bits in the column mask "colMask". + var col = findFirstZeroBit(colMask) + if (col == 64) col = 63 + colMask = colMask or (1L shl col) + event.column = col + activeList.add(event) + groupList.add(event) + val len: Int = activeList.size + if (maxCols < len) maxCols = len + } + for (ev in groupList) { + ev.maxColumns = maxCols + } } - long colMask = 0; - int maxCols = 0; - for (Event event : eventsList) { - // Process all-day events separately - if (event.drawAsAllday() != doAlldayEvents) - continue; - - if (!doAlldayEvents) { - colMask = removeNonAlldayActiveEvents( - event, activeList.iterator(), minimumDurationMillis, colMask); - } else { - colMask = removeAlldayActiveEvents(event, activeList.iterator(), colMask); + @JvmStatic private fun removeAlldayActiveEvents( + event: Event, + iter: Iterator, + colMask: Long + ): Long { + // Remove the inactive allday events. An event on the active list + // becomes inactive when the end day is less than the current event's + // start day. + var colMask = colMask + while (iter.hasNext()) { + val active = iter.next() + if (active.endDay < event.startDay) { + colMask = colMask and (1L shl active.column).inv() + iter.remove() + } } + return colMask + } - // If the active list is empty, then reset the max columns, clear - // the column bit mask, and empty the groupList. - if (activeList.isEmpty()) { - for (Event ev : groupList) { - ev.setMaxColumns(maxCols); + @JvmStatic private fun removeNonAlldayActiveEvents( + event: Event, + iter: Iterator, + minDurationMillis: Long, + colMask: Long + ): Long { + var colMask = colMask + val start = event.getStartMillis() + // Remove the inactive events. An event on the active list + // becomes inactive when its end time is less than or equal to + // the current event's start time. + while (iter.hasNext()) { + val active = iter.next() + val duration: Long = Math.max( + active.getEndMillis() - active.getStartMillis(), minDurationMillis + ) + if (active.getStartMillis() + duration <= start) { + colMask = colMask and (1L shl active.column).inv() + iter.remove() } - maxCols = 0; - colMask = 0; - groupList.clear(); } - - // Find the first empty column. Empty columns are represented by - // zero bits in the column mask "colMask". - int col = findFirstZeroBit(colMask); - if (col == 64) - col = 63; - colMask |= (1L << col); - event.setColumn(col); - activeList.add(event); - groupList.add(event); - int len = activeList.size(); - if (maxCols < len) - maxCols = len; + return colMask } - for (Event ev : groupList) { - ev.setMaxColumns(maxCols); - } - } - private static long removeAlldayActiveEvents(Event event, Iterator iter, long colMask) { - // Remove the inactive allday events. An event on the active list - // becomes inactive when the end day is less than the current event's - // start day. - while (iter.hasNext()) { - final Event active = iter.next(); - if (active.endDay < event.startDay) { - colMask &= ~(1L << active.getColumn()); - iter.remove(); + @JvmStatic fun findFirstZeroBit(`val`: Long): Int { + for (ii in 0..63) { + if (`val` and (1L shl ii) == 0L) return ii } + return 64 } - return colMask; - } - private static long removeNonAlldayActiveEvents( - Event event, Iterator iter, long minDurationMillis, long colMask) { - long start = event.getStartMillis(); - // Remove the inactive events. An event on the active list - // becomes inactive when its end time is less than or equal to - // the current event's start time. - while (iter.hasNext()) { - final Event active = iter.next(); - - final long duration = Math.max( - active.getEndMillis() - active.getStartMillis(), minDurationMillis); - if ((active.getStartMillis() + duration) <= start) { - colMask &= ~(1L << active.getColumn()); - iter.remove(); + init { + if (!Utils.isJellybeanOrLater()) { + EVENT_PROJECTION[PROJECTION_COLOR_INDEX] = Instances.CALENDAR_COLOR } } - return colMask; } - public static int findFirstZeroBit(long val) { - for (int ii = 0; ii < 64; ++ii) { - if ((val & (1L << ii)) == 0) - return ii; - } - return 64; + @JvmField var id: Long = 0 + @JvmField var color = 0 + @JvmField var title: CharSequence? = null + @JvmField var location: CharSequence? = null + @JvmField var allDay = false + @JvmField var organizer: String? = null + @JvmField var guestsCanModify = false + @JvmField var startDay = 0 // start Julian day + @JvmField var endDay = 0 // end Julian day + @JvmField var startTime = 0 // Start and end time are in minutes since midnight + @JvmField var endTime = 0 + @JvmField var startMillis = 0L // UTC milliseconds since the epoch + @JvmField var endMillis = 0L // UTC milliseconds since the epoch + @JvmField var column = 0 + @JvmField var maxColumns = 0 + @JvmField var hasAlarm = false + @JvmField var isRepeating = false + @JvmField var selfAttendeeStatus = 0 + + // The coordinates of the event rectangle drawn on the screen. + @JvmField var left = 0f + @JvmField var right = 0f + @JvmField var top = 0f + @JvmField var bottom = 0f + + // These 4 fields are used for navigating among events within the selected + // hour in the Day and Week view. + @JvmField var nextRight: Event? = null + @JvmField var nextLeft: Event? = null + @JvmField var nextUp: Event? = null + @JvmField var nextDown: Event? = null + @Override + @Throws(CloneNotSupportedException::class) + override fun clone(): Object { + super.clone() + val e = Event() + e.title = title + e.color = color + e.location = location + e.allDay = allDay + e.startDay = startDay + e.endDay = endDay + e.startTime = startTime + e.endTime = endTime + e.startMillis = startMillis + e.endMillis = endMillis + e.hasAlarm = hasAlarm + e.isRepeating = isRepeating + e.selfAttendeeStatus = selfAttendeeStatus + e.organizer = organizer + e.guestsCanModify = guestsCanModify + return e as Object } - public final void dump() { - Log.e("Cal", "+-----------------------------------------+"); - Log.e("Cal", "+ id = " + id); - Log.e("Cal", "+ color = " + color); - Log.e("Cal", "+ title = " + title); - Log.e("Cal", "+ location = " + location); - Log.e("Cal", "+ allDay = " + allDay); - Log.e("Cal", "+ startDay = " + startDay); - 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); + fun copyTo(dest: Event) { + dest.id = id + dest.title = title + dest.color = color + dest.location = location + dest.allDay = allDay + dest.startDay = startDay + dest.endDay = endDay + dest.startTime = startTime + dest.endTime = endTime + dest.startMillis = startMillis + dest.endMillis = endMillis + dest.hasAlarm = hasAlarm + dest.isRepeating = isRepeating + dest.selfAttendeeStatus = selfAttendeeStatus + dest.organizer = organizer + dest.guestsCanModify = guestsCanModify } - public final boolean intersects(int julianDay, int startMinute, - int endMinute) { + fun dump() { + Log.e("Cal", "+-----------------------------------------+") + Log.e("Cal", "+ id = $id") + Log.e("Cal", "+ color = $color") + Log.e("Cal", "+ title = $title") + Log.e("Cal", "+ location = $location") + Log.e("Cal", "+ allDay = $allDay") + Log.e("Cal", "+ startDay = $startDay") + 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") + } + + fun intersects( + julianDay: Int, + startMinute: Int, + endMinute: Int + ): Boolean { if (endDay < julianDay) { - return false; + return false } - if (startDay > julianDay) { - return false; + return false } - if (endDay == julianDay) { if (endTime < startMinute) { - return false; + return false } // An event that ends at the start minute should not be considered // as intersecting the given time span, but don't exclude // zero-length (or very short) events. - if (endTime == startMinute - && (startTime != endTime || startDay != endDay)) { - return false; + if (endTime == startMinute && + (startTime != endTime || startDay != endDay)) { + return false } } - - if (startDay == julianDay && startTime > endMinute) { - return false; - } - - return true; + return !(startDay == julianDay && startTime > endMinute) } /** @@ -588,55 +595,46 @@ public class Event implements Cloneable { * * @return the event title and location as a String */ - public String getTitleAndLocation() { - String text = title.toString(); - - // Append the location to the title, unless the title ends with the - // location (for example, "meeting in building 42" ends with the - // location). - if (location != null) { - String locationString = location.toString(); - if (!text.endsWith(locationString)) { - text += ", " + locationString; + val titleAndLocation: String + get() { + var text = title.toString() + + // Append the location to the title, unless the title ends with the + // location (for example, "meeting in building 42" ends with the + // location). + if (location != null) { + val locationString = location.toString() + if (!text.endsWith(locationString)) { + text += ", $locationString" + } } + return text } - return text; - } - - public void setColumn(int column) { - mColumn = column; - } - - public int getColumn() { - return mColumn; - } - - public void setMaxColumns(int maxColumns) { - mMaxColumns = maxColumns; - } - public int getMaxColumns() { - return mMaxColumns; + // TODO(damianpatel): this getter will likely not be + // needed once DayView.java is converted + fun getColumn(): Int { + return column } - public void setStartMillis(long startMillis) { - this.startMillis = startMillis; + fun setStartMillis(startMillis: Long) { + this.startMillis = startMillis } - public long getStartMillis() { - return startMillis; + fun getStartMillis(): Long { + return startMillis } - public void setEndMillis(long endMillis) { - this.endMillis = endMillis; + fun setEndMillis(endMillis: Long) { + this.endMillis = endMillis } - public long getEndMillis() { - return endMillis; + fun getEndMillis(): Long { + return endMillis } - public boolean drawAsAllday() { + fun drawAsAllday(): Boolean { // Use >= so we'll pick up Exchange allday events - return allDay || endMillis - startMillis >= DateUtils.DAY_IN_MILLIS; + return allDay || endMillis - startMillis >= DateUtils.DAY_IN_MILLIS } -} +} \ No newline at end of file diff --git a/src/com/android/calendar/EventGeometry.kt b/src/com/android/calendar/EventGeometry.kt index b6af49a0..43fc3e77 100644 --- a/src/com/android/calendar/EventGeometry.kt +++ b/src/com/android/calendar/EventGeometry.kt @@ -65,8 +65,8 @@ class EventGeometry { if (endDay > date) { endTime = DayView.MINUTES_PER_DAY } - val col: Int = event.getColumn() - val maxCols: Int = event.getMaxColumns() + val col: Int = event.column + val maxCols: Int = event.maxColumns val startHour = startTime / 60 var endHour = endTime / 60 diff --git a/src/com/android/calendar/EventLoader.kt b/src/com/android/calendar/EventLoader.kt index ccfcaf15..a05e8a2e 100644 --- a/src/com/android/calendar/EventLoader.kt +++ b/src/com/android/calendar/EventLoader.kt @@ -31,7 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger class EventLoader(context: Context) { private val mContext: Context private val mHandler: Handler = Handler() - private val mSequenceNumber: AtomicInteger = AtomicInteger() + private val mSequenceNumber: AtomicInteger? = AtomicInteger() private val mLoaderQueue: LinkedBlockingQueue private var mLoaderThread: LoaderThread? = null private val mResolver: ContentResolver @@ -52,9 +52,12 @@ class EventLoader(context: Context) { * and filling in the eventDays array. * */ - private class LoadEventDaysRequest(var startDay: Int, var numDays: Int, - var eventDays: BooleanArray, - uiCallback: Runnable) : LoadRequest { + private class LoadEventDaysRequest( + var startDay: Int, + var numDays: Int, + var eventDays: BooleanArray, + uiCallback: Runnable + ) : LoadRequest { var uiCallback: Runnable @Override override fun processRequest(eventLoader: EventLoader?) { @@ -64,17 +67,17 @@ class EventLoader(context: Context) { // Clear the event days Arrays.fill(eventDays, false) - //query which days have events + // query which days have events val cursor: Cursor = EventDays.query(cr, startDay, numDays, PROJECTION) try { val startDayColumnIndex: Int = cursor.getColumnIndexOrThrow(EventDays.STARTDAY) val endDayColumnIndex: Int = cursor.getColumnIndexOrThrow(EventDays.ENDDAY) - //Set all the days with events to true + // Set all the days with events to true while (cursor.moveToNext()) { val firstDay: Int = cursor.getInt(startDayColumnIndex) val lastDay: Int = cursor.getInt(endDayColumnIndex) - //we want the entire range the event occurs, but only within the month + // we want the entire range the event occurs, but only within the month val firstIndex: Int = Math.max(firstDay - startDay, 0) val lastIndex: Int = Math.min(lastDay - startDay, 30) for (i in firstIndex..lastIndex) { @@ -107,10 +110,15 @@ class EventLoader(context: Context) { } } - private class LoadEventsRequest(var id: Int, var startDay: Int, var numDays: Int, - events: ArrayList, successCallback: Runnable, - cancelCallback: Runnable) : LoadRequest { - var events: ArrayList + private class LoadEventsRequest( + var id: Int, + var startDay: Int, + var numDays: Int, + events: ArrayList, + successCallback: Runnable, + cancelCallback: Runnable + ) : LoadRequest { + var events: ArrayList var successCallback: Runnable var cancelCallback: Runnable @Override @@ -138,8 +146,10 @@ class EventLoader(context: Context) { } } - private class LoaderThread(queue: LinkedBlockingQueue, - eventLoader: EventLoader) : Thread() { + private class LoaderThread( + queue: LinkedBlockingQueue, + eventLoader: EventLoader + ) : Thread() { var mQueue: LinkedBlockingQueue var mEventLoader: EventLoader fun shutdown() { @@ -210,13 +220,18 @@ class EventLoader(context: Context) { * created are used, and the most recent call's worth of data is loaded into events and posted * via the uiCallback. */ - fun loadEventsInBackground(numDays: Int, events: ArrayList, - startDay: Int, successCallback: Runnable, cancelCallback: Runnable) { + fun loadEventsInBackground( + numDays: Int, + events: ArrayList, + startDay: Int, + successCallback: Runnable, + cancelCallback: Runnable + ) { // Increment the sequence number for requests. We don't care if the // sequence numbers wrap around because we test for equality with the // latest one. - val id: Int = mSequenceNumber.incrementAndGet() + val id: Int = mSequenceNumber?.incrementAndGet() as Int // Send the load request to the background thread val request = LoadEventsRequest(id, startDay, numDays, @@ -241,8 +256,12 @@ class EventLoader(context: Context) { * @param eventDay Whether or not an event exists on that day * @param uiCallback What to do when done (log data, redraw screen) */ - fun loadEventDaysInBackground(startDay: Int, numDays: Int, eventDays: BooleanArray, - uiCallback: Runnable) { + fun loadEventDaysInBackground( + startDay: Int, + numDays: Int, + eventDays: BooleanArray, + uiCallback: Runnable + ) { // Send load request to the background thread val request = LoadEventDaysRequest(startDay, numDays, eventDays, uiCallback) diff --git a/src/com/android/calendar/GeneralPreferences.kt b/src/com/android/calendar/GeneralPreferences.kt index 259a8918..dd4c9550 100644 --- a/src/com/android/calendar/GeneralPreferences.kt +++ b/src/com/android/calendar/GeneralPreferences.kt @@ -21,7 +21,6 @@ import android.app.backup.BackupManager import android.content.Context import android.content.Intent import android.content.SharedPreferences -import android.content.SharedPreferences.Editor import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.media.Ringtone import android.media.RingtoneManager @@ -37,13 +36,10 @@ import android.preference.PreferenceCategory import android.preference.PreferenceFragment import android.preference.PreferenceManager import android.preference.PreferenceScreen -import android.preference.RingtonePreference import android.provider.CalendarContract import android.provider.CalendarContract.CalendarCache -import android.provider.SearchRecentSuggestions import android.text.TextUtils import android.text.format.Time -import android.widget.Toast import com.android.calendar.alerts.AlertReceiver import com.android.timezonepicker.TimeZoneInfo import com.android.timezonepicker.TimeZonePickerDialog @@ -71,7 +67,7 @@ class GeneralPreferences : PreferenceFragment(), OnSharedPreferenceChangeListene // Make sure to always use the same preferences file regardless of the package name // we're running under val preferenceManager: PreferenceManager = getPreferenceManager() - val sharedPreferences: SharedPreferences = getSharedPreferences(activity) + val sharedPreferences: SharedPreferences? = getSharedPreferences(activity) preferenceManager.setSharedPreferencesName(SHARED_PREFS_NAME) // Load the preferences from an XML resource @@ -106,7 +102,7 @@ class GeneralPreferences : PreferenceFragment(), OnSharedPreferenceChangeListene mTimeZoneId = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()) } - mHomeTZ?.setOnPreferenceClickListener(object: Preference.OnPreferenceClickListener { + mHomeTZ?.setOnPreferenceClickListener(object : Preference.OnPreferenceClickListener { @Override override fun onPreferenceClick(preference: Preference?): Boolean { showTimezoneDialog() @@ -247,13 +243,13 @@ class GeneralPreferences : PreferenceFragment(), OnSharedPreferenceChangeListene * set of keys and values. * @param prefs the preferences to upgrade */ - private fun migrateOldPreferences(prefs: SharedPreferences) { + private fun migrateOldPreferences(prefs: SharedPreferences?) { // If needed, migrate vibration setting from a previous version mVibrate?.setChecked(Utils.getDefaultVibrate(getActivity(), prefs)) // If needed, migrate the old alerts type settin - if (!prefs.contains(KEY_ALERTS) && prefs.contains(KEY_ALERTS_TYPE)) { - val type: String? = prefs.getString(KEY_ALERTS_TYPE, ALERT_TYPE_STATUS_BAR) + if (prefs?.contains(KEY_ALERTS) == false && prefs?.contains(KEY_ALERTS_TYPE) == true) { + val type: String? = prefs?.getString(KEY_ALERTS_TYPE, ALERT_TYPE_STATUS_BAR) if (type.equals(ALERT_TYPE_OFF)) { mAlert?.setChecked(false) mPopup?.setChecked(false) @@ -268,7 +264,7 @@ class GeneralPreferences : PreferenceFragment(), OnSharedPreferenceChangeListene mPopup?.setEnabled(true) } // clear out the old setting - prefs.edit().remove(KEY_ALERTS_TYPE).commit() + prefs?.edit().remove(KEY_ALERTS_TYPE).commit() } } @@ -289,7 +285,9 @@ class GeneralPreferences : PreferenceFragment(), OnSharedPreferenceChangeListene @Override override fun onPreferenceTreeClick( - preferenceScreen: PreferenceScreen?, preference: Preference): Boolean { + preferenceScreen: PreferenceScreen?, + preference: Preference + ): Boolean { val key: String = preference.getKey() return super.onPreferenceTreeClick(preferenceScreen, preference) } @@ -364,11 +362,10 @@ class GeneralPreferences : PreferenceFragment(), OnSharedPreferenceChangeListene // This should match the XML file. const val DEFAULT_RINGTONE = "content://settings/system/notification_sound" - /** Return a properly configured SharedPreferences instance */ @JvmStatic - fun getSharedPreferences(context: Context): SharedPreferences { - return context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) + fun getSharedPreferences(context: Context?): SharedPreferences? { + return context?.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) } /** Set the default shared preferences in the proper context */ diff --git a/src/com/android/calendar/alerts/QuickResponseActivity.kt b/src/com/android/calendar/alerts/QuickResponseActivity.kt index a3d27e83..afccaffd 100644 --- a/src/com/android/calendar/alerts/QuickResponseActivity.kt +++ b/src/com/android/calendar/alerts/QuickResponseActivity.kt @@ -38,12 +38,12 @@ class QuickResponseActivity : ListActivity(), OnItemClickListener { @Override protected override fun onCreate(icicle: Bundle?) { super.onCreate(icicle) - val intent: Intent = getIntent() + val intent: Intent? = getIntent() if (intent == null) { finish() return } - mEventId = intent.getLongExtra(EXTRA_EVENT_ID, -1) + mEventId = intent?.getLongExtra(EXTRA_EVENT_ID, -1) as Long if (mEventId == -1L) { finish() return -- cgit v1.2.3 From d101b2ff3d8fd4b2c25ec998dad02336104b0ea0 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Wed, 23 Jun 2021 21:42:41 +0000 Subject: AOSP/Calendar - Add Kotlin copy of CalendarData.java and SimpleWeekView.java Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: Ie7de49ede9e883c9f6cca688ba95cb90a1b594b2 --- src/com/android/calendar/CalendarData.kt | 28 ++ src/com/android/calendar/month/SimpleWeekView.kt | 551 +++++++++++++++++++++++ 2 files changed, 579 insertions(+) create mode 100644 src/com/android/calendar/CalendarData.kt create mode 100644 src/com/android/calendar/month/SimpleWeekView.kt diff --git a/src/com/android/calendar/CalendarData.kt b/src/com/android/calendar/CalendarData.kt new file mode 100644 index 00000000..5c8456fa --- /dev/null +++ b/src/com/android/calendar/CalendarData.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2006 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; + +public final class CalendarData { + static final String[] s12HoursNoAmPm = { "12", "1", "2", "3", "4", + "5", "6", "7", "8", "9", "10", "11", "12", + "1", "2", "3", "4", "5", "6", "7", "8", + "9", "10", "11", "12" }; + + static final String[] s24Hours = { "00", "01", "02", "03", "04", "05", + "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", + "17", "18", "19", "20", "21", "22", "23", "00" }; +} diff --git a/src/com/android/calendar/month/SimpleWeekView.kt b/src/com/android/calendar/month/SimpleWeekView.kt new file mode 100644 index 00000000..4d0c09f4 --- /dev/null +++ b/src/com/android/calendar/month/SimpleWeekView.kt @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2010 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.month; + +import com.android.calendar.R; +import com.android.calendar.Utils; + +import android.app.Service; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.Style; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.view.MotionEvent; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; + +import java.security.InvalidParameterException; +import java.util.HashMap; + +/** + *

+ * This is a dynamic view for drawing a single week. It can be configured to + * display the week number, start the week on a given day, or show a reduced + * number of days. It is intended for use as a single view within a ListView. + * See {@link SimpleWeeksAdapter} for usage. + *

+ */ +public class SimpleWeekView extends View { + private static final String TAG = "MonthView"; + + /** + * These params can be passed into the view to control how it appears. + * {@link #VIEW_PARAMS_WEEK} is the only required field, though the default + * values are unlikely to fit most layouts correctly. + */ + /** + * This sets the height of this week in pixels + */ + public static final String VIEW_PARAMS_HEIGHT = "height"; + /** + * This specifies the position (or weeks since the epoch) of this week, + * calculated using {@link Utils#getWeeksSinceEpochFromJulianDay} + */ + public static final String VIEW_PARAMS_WEEK = "week"; + /** + * This sets one of the days in this view as selected {@link Time#SUNDAY} + * through {@link Time#SATURDAY}. + */ + public static final String VIEW_PARAMS_SELECTED_DAY = "selected_day"; + /** + * Which day the week should start on. {@link Time#SUNDAY} through + * {@link Time#SATURDAY}. + */ + public static final String VIEW_PARAMS_WEEK_START = "week_start"; + /** + * How many days to display at a time. Days will be displayed starting with + * {@link #mWeekStart}. + */ + public static final String VIEW_PARAMS_NUM_DAYS = "num_days"; + /** + * Which month is currently in focus, as defined by {@link Time#month} + * [0-11]. + */ + public static final String VIEW_PARAMS_FOCUS_MONTH = "focus_month"; + /** + * If this month should display week numbers. false if 0, true otherwise. + */ + public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num"; + + protected static int DEFAULT_HEIGHT = 32; + protected static int MIN_HEIGHT = 10; + protected static final int DEFAULT_SELECTED_DAY = -1; + protected static final int DEFAULT_WEEK_START = Time.SUNDAY; + protected static final int DEFAULT_NUM_DAYS = 7; + protected static final int DEFAULT_SHOW_WK_NUM = 0; + protected static final int DEFAULT_FOCUS_MONTH = -1; + + protected static int DAY_SEPARATOR_WIDTH = 1; + + protected static int MINI_DAY_NUMBER_TEXT_SIZE = 14; + protected static int MINI_WK_NUMBER_TEXT_SIZE = 12; + protected static int MINI_TODAY_NUMBER_TEXT_SIZE = 18; + protected static int MINI_TODAY_OUTLINE_WIDTH = 2; + protected static int WEEK_NUM_MARGIN_BOTTOM = 4; + + // used for scaling to the device density + protected static float mScale = 0; + + // affects the padding on the sides of this view + protected int mPadding = 0; + + protected Rect r = new Rect(); + protected Paint p = new Paint(); + protected Paint mMonthNumPaint; + protected Drawable mSelectedDayLine; + + // Cache the number strings so we don't have to recompute them each time + protected String[] mDayNumbers; + // Quick lookup for checking which days are in the focus month + protected boolean[] mFocusDay; + // Quick lookup for checking which days are in an odd month (to set a different background) + protected boolean[] mOddMonth; + // The Julian day of the first day displayed by this item + protected int mFirstJulianDay = -1; + // The month of the first day in this week + protected int mFirstMonth = -1; + // The month of the last day in this week + protected int mLastMonth = -1; + // The position of this week, equivalent to weeks since the week of Jan 1st, + // 1970 + protected int mWeek = -1; + // Quick reference to the width of this view, matches parent + protected int mWidth; + // The height this view should draw at in pixels, set by height param + protected int mHeight = DEFAULT_HEIGHT; + // Whether the week number should be shown + protected boolean mShowWeekNum = false; + // If this view contains the selected day + protected boolean mHasSelectedDay = false; + // If this view contains the today + protected boolean mHasToday = false; + // Which day is selected [0-6] or -1 if no day is selected + protected int mSelectedDay = DEFAULT_SELECTED_DAY; + // Which day is today [0-6] or -1 if no day is today + protected int mToday = DEFAULT_SELECTED_DAY; + // Which day of the week to start on [0-6] + protected int mWeekStart = DEFAULT_WEEK_START; + // How many days to display + protected int mNumDays = DEFAULT_NUM_DAYS; + // The number of days + a spot for week number if it is displayed + protected int mNumCells = mNumDays; + // The left edge of the selected day + protected int mSelectedLeft = -1; + // The right edge of the selected day + protected int mSelectedRight = -1; + // The timezone to display times/dates in (used for determining when Today + // is) + protected String mTimeZone = Time.getCurrentTimezone(); + + protected int mBGColor; + protected int mSelectedWeekBGColor; + protected int mFocusMonthColor; + protected int mOtherMonthColor; + protected int mDaySeparatorColor; + protected int mTodayOutlineColor; + protected int mWeekNumColor; + + public SimpleWeekView(Context context) { + super(context); + + Resources res = context.getResources(); + + mBGColor = res.getColor(R.color.month_bgcolor); + mSelectedWeekBGColor = res.getColor(R.color.month_selected_week_bgcolor); + mFocusMonthColor = res.getColor(R.color.month_mini_day_number); + mOtherMonthColor = res.getColor(R.color.month_other_month_day_number); + mDaySeparatorColor = res.getColor(R.color.month_grid_lines); + mTodayOutlineColor = res.getColor(R.color.mini_month_today_outline_color); + mWeekNumColor = res.getColor(R.color.month_week_num_color); + mSelectedDayLine = res.getDrawable(R.drawable.dayline_minical_holo_light); + + if (mScale == 0) { + mScale = context.getResources().getDisplayMetrics().density; + if (mScale != 1) { + DEFAULT_HEIGHT *= mScale; + MIN_HEIGHT *= mScale; + MINI_DAY_NUMBER_TEXT_SIZE *= mScale; + MINI_TODAY_NUMBER_TEXT_SIZE *= mScale; + MINI_TODAY_OUTLINE_WIDTH *= mScale; + WEEK_NUM_MARGIN_BOTTOM *= mScale; + DAY_SEPARATOR_WIDTH *= mScale; + MINI_WK_NUMBER_TEXT_SIZE *= mScale; + } + } + + // Sets up any standard paints that will be used + initView(); + } + + /** + * Sets all the parameters for displaying this week. The only required + * parameter is the week number. Other parameters have a default value and + * will only update if a new value is included, except for focus month, + * which will always default to no focus month if no value is passed in. See + * {@link #VIEW_PARAMS_HEIGHT} for more info on parameters. + * + * @param params A map of the new parameters, see + * {@link #VIEW_PARAMS_HEIGHT} + * @param tz The time zone this view should reference times in + */ + public void setWeekParams(HashMap params, String tz) { + if (!params.containsKey(VIEW_PARAMS_WEEK)) { + throw new InvalidParameterException("You must specify the week number for this view"); + } + setTag(params); + mTimeZone = tz; + // We keep the current value for any params not present + if (params.containsKey(VIEW_PARAMS_HEIGHT)) { + mHeight = params.get(VIEW_PARAMS_HEIGHT); + if (mHeight < MIN_HEIGHT) { + mHeight = MIN_HEIGHT; + } + } + if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) { + mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY); + } + mHasSelectedDay = mSelectedDay != -1; + if (params.containsKey(VIEW_PARAMS_NUM_DAYS)) { + mNumDays = params.get(VIEW_PARAMS_NUM_DAYS); + } + if (params.containsKey(VIEW_PARAMS_SHOW_WK_NUM)) { + if (params.get(VIEW_PARAMS_SHOW_WK_NUM) != 0) { + mShowWeekNum = true; + } else { + mShowWeekNum = false; + } + } + mNumCells = mShowWeekNum ? mNumDays + 1 : mNumDays; + + // Allocate space for caching the day numbers and focus values + mDayNumbers = new String[mNumCells]; + mFocusDay = new boolean[mNumCells]; + mOddMonth = new boolean[mNumCells]; + mWeek = params.get(VIEW_PARAMS_WEEK); + int julianMonday = Utils.getJulianMondayFromWeeksSinceEpoch(mWeek); + Time time = new Time(tz); + time.setJulianDay(julianMonday); + + // If we're showing the week number calculate it based on Monday + int i = 0; + if (mShowWeekNum) { + mDayNumbers[0] = Integer.toString(time.getWeekNumber()); + i++; + } + + if (params.containsKey(VIEW_PARAMS_WEEK_START)) { + mWeekStart = params.get(VIEW_PARAMS_WEEK_START); + } + + // Now adjust our starting day based on the start day of the week + // If the week is set to start on a Saturday the first week will be + // Dec 27th 1969 -Jan 2nd, 1970 + if (time.weekDay != mWeekStart) { + int diff = time.weekDay - mWeekStart; + if (diff < 0) { + diff += 7; + } + time.monthDay -= diff; + time.normalize(true); + } + + mFirstJulianDay = Time.getJulianDay(time.toMillis(true), time.gmtoff); + mFirstMonth = time.month; + + // Figure out what day today is + Time today = new Time(tz); + today.setToNow(); + mHasToday = false; + mToday = -1; + + int focusMonth = params.containsKey(VIEW_PARAMS_FOCUS_MONTH) ? params.get( + VIEW_PARAMS_FOCUS_MONTH) + : DEFAULT_FOCUS_MONTH; + + for (; i < mNumCells; i++) { + if (time.monthDay == 1) { + mFirstMonth = time.month; + } + mOddMonth [i] = (time.month %2) == 1; + if (time.month == focusMonth) { + mFocusDay[i] = true; + } else { + mFocusDay[i] = false; + } + if (time.year == today.year && time.yearDay == today.yearDay) { + mHasToday = true; + mToday = i; + } + mDayNumbers[i] = Integer.toString(time.monthDay++); + time.normalize(true); + } + // We do one extra add at the end of the loop, if that pushed us to a + // new month undo it + if (time.monthDay == 1) { + time.monthDay--; + time.normalize(true); + } + mLastMonth = time.month; + + updateSelectionPositions(); + } + + /** + * Sets up the text and style properties for painting. Override this if you + * want to use a different paint. + */ + protected void initView() { + p.setFakeBoldText(false); + p.setAntiAlias(true); + p.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); + p.setStyle(Style.FILL); + + mMonthNumPaint = new Paint(); + mMonthNumPaint.setFakeBoldText(true); + mMonthNumPaint.setAntiAlias(true); + mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); + mMonthNumPaint.setColor(mFocusMonthColor); + mMonthNumPaint.setStyle(Style.FILL); + mMonthNumPaint.setTextAlign(Align.CENTER); + } + + /** + * Returns the month of the first day in this week + * + * @return The month the first day of this view is in + */ + public int getFirstMonth() { + return mFirstMonth; + } + + /** + * Returns the month of the last day in this week + * + * @return The month the last day of this view is in + */ + public int getLastMonth() { + return mLastMonth; + } + + /** + * Returns the julian day of the first day in this view. + * + * @return The julian day of the first day in the view. + */ + public int getFirstJulianDay() { + return mFirstJulianDay; + } + + /** + * Calculates the day that the given x position is in, accounting for week + * number. Returns a Time referencing that day or null if + * + * @param x The x position of the touch event + * @return A time object for the tapped day or null if the position wasn't + * in a day + */ + public Time getDayFromLocation(float x) { + int dayStart = mShowWeekNum ? (mWidth - mPadding * 2) / mNumCells + mPadding : mPadding; + if (x < dayStart || x > mWidth - mPadding) { + return null; + } + // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels + int dayPosition = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)); + int day = mFirstJulianDay + dayPosition; + + Time time = new Time(mTimeZone); + if (mWeek == 0) { + // This week is weird... + if (day < Time.EPOCH_JULIAN_DAY) { + day++; + } else if (day == Time.EPOCH_JULIAN_DAY) { + time.set(1, 0, 1970); + time.normalize(true); + return time; + } + } + + time.setJulianDay(day); + return time; + } + + @Override + protected void onDraw(Canvas canvas) { + drawBackground(canvas); + drawWeekNums(canvas); + drawDaySeparators(canvas); + } + + /** + * This draws the selection highlight if a day is selected in this week. + * Override this method if you wish to have a different background drawn. + * + * @param canvas The canvas to draw on + */ + protected void drawBackground(Canvas canvas) { + if (mHasSelectedDay) { + p.setColor(mSelectedWeekBGColor); + p.setStyle(Style.FILL); + } else { + return; + } + r.top = 1; + r.bottom = mHeight - 1; + r.left = mPadding; + r.right = mSelectedLeft; + canvas.drawRect(r, p); + r.left = mSelectedRight; + r.right = mWidth - mPadding; + canvas.drawRect(r, p); + } + + /** + * Draws the week and month day numbers for this week. Override this method + * if you need different placement. + * + * @param canvas The canvas to draw on + */ + protected void drawWeekNums(Canvas canvas) { + int y = ((mHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2) - DAY_SEPARATOR_WIDTH; + int nDays = mNumCells; + + int i = 0; + int divisor = 2 * nDays; + if (mShowWeekNum) { + p.setTextSize(MINI_WK_NUMBER_TEXT_SIZE); + p.setStyle(Style.FILL); + p.setTextAlign(Align.CENTER); + p.setAntiAlias(true); + p.setColor(mWeekNumColor); + int x = (mWidth - mPadding * 2) / divisor + mPadding; + canvas.drawText(mDayNumbers[0], x, y, p); + i++; + } + + boolean isFocusMonth = mFocusDay[i]; + mMonthNumPaint.setColor(isFocusMonth ? mFocusMonthColor : mOtherMonthColor); + mMonthNumPaint.setFakeBoldText(false); + for (; i < nDays; i++) { + if (mFocusDay[i] != isFocusMonth) { + isFocusMonth = mFocusDay[i]; + mMonthNumPaint.setColor(isFocusMonth ? mFocusMonthColor : mOtherMonthColor); + } + if (mHasToday && mToday == i) { + mMonthNumPaint.setTextSize(MINI_TODAY_NUMBER_TEXT_SIZE); + mMonthNumPaint.setFakeBoldText(true); + } + int x = (2 * i + 1) * (mWidth - mPadding * 2) / (divisor) + mPadding; + canvas.drawText(mDayNumbers[i], x, y, mMonthNumPaint); + if (mHasToday && mToday == i) { + mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); + mMonthNumPaint.setFakeBoldText(false); + } + } + } + + /** + * Draws a horizontal line for separating the weeks. Override this method if + * you want custom separators. + * + * @param canvas The canvas to draw on + */ + protected void drawDaySeparators(Canvas canvas) { + if (mHasSelectedDay) { + r.top = 1; + r.bottom = mHeight - 1; + r.left = mSelectedLeft + 1; + r.right = mSelectedRight - 1; + p.setStrokeWidth(MINI_TODAY_OUTLINE_WIDTH); + p.setStyle(Style.STROKE); + p.setColor(mTodayOutlineColor); + canvas.drawRect(r, p); + } + if (mShowWeekNum) { + p.setColor(mDaySeparatorColor); + p.setStrokeWidth(DAY_SEPARATOR_WIDTH); + + int x = (mWidth - mPadding * 2) / mNumCells + mPadding; + canvas.drawLine(x, 0, x, mHeight, p); + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + mWidth = w; + updateSelectionPositions(); + } + + /** + * This calculates the positions for the selected day lines. + */ + protected void updateSelectionPositions() { + if (mHasSelectedDay) { + int selectedPosition = mSelectedDay - mWeekStart; + if (selectedPosition < 0) { + selectedPosition += 7; + } + if (mShowWeekNum) { + selectedPosition++; + } + mSelectedLeft = selectedPosition * (mWidth - mPadding * 2) / mNumCells + + mPadding; + mSelectedRight = (selectedPosition + 1) * (mWidth - mPadding * 2) / mNumCells + + mPadding; + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight); + } + + @Override + public boolean onHoverEvent(MotionEvent event) { + Context context = getContext(); + // only send accessibility events if accessibility and exploration are + // on. + AccessibilityManager am = (AccessibilityManager) context + .getSystemService(Service.ACCESSIBILITY_SERVICE); + if (!am.isEnabled() || !am.isTouchExplorationEnabled()) { + return super.onHoverEvent(event); + } + if (event.getAction() != MotionEvent.ACTION_HOVER_EXIT) { + Time hover = getDayFromLocation(event.getX()); + if (hover != null + && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) != 0)) { + Long millis = hover.toMillis(true); + String date = Utils.formatDateRange(context, millis, millis, + DateUtils.FORMAT_SHOW_DATE); + AccessibilityEvent accessEvent = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); + accessEvent.getText().add(date); + sendAccessibilityEventUnchecked(accessEvent); + mLastHoverTime = hover; + } + } + return true; + } + + Time mLastHoverTime = null; +} \ No newline at end of file -- cgit v1.2.3 From 199495aad0402fc0e48417ac4ab882d6a5c80b5c Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Wed, 23 Jun 2021 22:04:16 +0000 Subject: AOSP/Calendar - Add Kotlin code for CalendarData.kt and SimpleWeekView.kt. Also uploaded corresponding Android.bp file A few functions required override modifiers and a few variable declarations to include nullability references. I also added @JvmField annotations to the instance variables in both classes. Because SimpleWeekView is a parent class to MonthWeekEventsView, a few overriden methods were marked as "open" as opposed to "final". Test: manual (ran build and unit tests) and CTS testing suite $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.115 OK (22 tests) CTS TESTING: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ============================================ ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 7m 20s Total aggregated tests run time: 7m 20s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.85 tests/sec [376 tests / 440083 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 11399 ms || clean = 2964 ms Total preparation time: 11s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 14m 18s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: I78229b285a9b8b28ad594d4801b3d8c15266ef54 --- Android.bp | 2 + src/com/android/calendar/CalendarData.kt | 25 +- src/com/android/calendar/month/SimpleWeekView.kt | 709 +++++++++++------------ 3 files changed, 362 insertions(+), 374 deletions(-) diff --git a/Android.bp b/Android.bp index bfc0eaf7..35866ce5 100644 --- a/Android.bp +++ b/Android.bp @@ -36,9 +36,11 @@ exclude_srcsd = [ exclude_srcsm = [ "src/**/calendar/month/MonthListView.java", + "src/**/calendar/month/SimpleWeekView.java", "src/**/calendar/AsyncQueryServiceHelper.java", "src/**/calendar/CalendarApplication.java", "src/**/calendar/CalendarBackupAgent.java", + "src/**/calendar/CalendarData.java", "src/**/calendar/CalendarUtils.java", "src/**/calendar/CalendarViewAdapter.java", "src/**/calendar/DayFragment.java", diff --git a/src/com/android/calendar/CalendarData.kt b/src/com/android/calendar/CalendarData.kt index 5c8456fa..7370f2e2 100644 --- a/src/com/android/calendar/CalendarData.kt +++ b/src/com/android/calendar/CalendarData.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,16 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar -package com.android.calendar; +object CalendarData { + @JvmField + val s12HoursNoAmPm = arrayOf("12", "1", "2", "3", "4", + "5", "6", "7", "8", "9", "10", "11", "12", + "1", "2", "3", "4", "5", "6", "7", "8", + "9", "10", "11", "12") -public final class CalendarData { - static final String[] s12HoursNoAmPm = { "12", "1", "2", "3", "4", - "5", "6", "7", "8", "9", "10", "11", "12", - "1", "2", "3", "4", "5", "6", "7", "8", - "9", "10", "11", "12" }; - - static final String[] s24Hours = { "00", "01", "02", "03", "04", "05", - "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", - "17", "18", "19", "20", "21", "22", "23", "00" }; -} + @JvmField + val s24Hours = arrayOf("00", "01", "02", "03", "04", "05", + "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", + "17", "18", "19", "20", "21", "22", "23", "00") +} \ No newline at end of file diff --git a/src/com/android/calendar/month/SimpleWeekView.kt b/src/com/android/calendar/month/SimpleWeekView.kt index 4d0c09f4..71313d10 100644 --- a/src/com/android/calendar/month/SimpleWeekView.kt +++ b/src/com/android/calendar/month/SimpleWeekView.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,348 +13,244 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar.month; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.app.Service; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Paint.Style; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.view.MotionEvent; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; - -import java.security.InvalidParameterException; -import java.util.HashMap; +package com.android.calendar.month + +import com.android.calendar.R +import com.android.calendar.Utils +import android.app.Service +import android.content.Context +import android.content.res.Resources +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Paint.Align +import android.graphics.Paint.Style +import android.graphics.Rect +import android.graphics.drawable.Drawable +import android.text.format.DateUtils +import android.text.format.Time +import android.view.MotionEvent +import android.view.View +import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityManager +import java.security.InvalidParameterException +import java.util.HashMap /** - *

+ * + * * This is a dynamic view for drawing a single week. It can be configured to * display the week number, start the week on a given day, or show a reduced * number of days. It is intended for use as a single view within a ListView. - * See {@link SimpleWeeksAdapter} for usage. - *

+ * See [SimpleWeeksAdapter] for usage. + * */ -public class SimpleWeekView extends View { - private static final String TAG = "MonthView"; - - /** - * These params can be passed into the view to control how it appears. - * {@link #VIEW_PARAMS_WEEK} is the only required field, though the default - * values are unlikely to fit most layouts correctly. - */ - /** - * This sets the height of this week in pixels - */ - public static final String VIEW_PARAMS_HEIGHT = "height"; - /** - * This specifies the position (or weeks since the epoch) of this week, - * calculated using {@link Utils#getWeeksSinceEpochFromJulianDay} - */ - public static final String VIEW_PARAMS_WEEK = "week"; - /** - * This sets one of the days in this view as selected {@link Time#SUNDAY} - * through {@link Time#SATURDAY}. - */ - public static final String VIEW_PARAMS_SELECTED_DAY = "selected_day"; - /** - * Which day the week should start on. {@link Time#SUNDAY} through - * {@link Time#SATURDAY}. - */ - public static final String VIEW_PARAMS_WEEK_START = "week_start"; - /** - * How many days to display at a time. Days will be displayed starting with - * {@link #mWeekStart}. - */ - public static final String VIEW_PARAMS_NUM_DAYS = "num_days"; - /** - * Which month is currently in focus, as defined by {@link Time#month} - * [0-11]. - */ - public static final String VIEW_PARAMS_FOCUS_MONTH = "focus_month"; - /** - * If this month should display week numbers. false if 0, true otherwise. - */ - public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num"; - - protected static int DEFAULT_HEIGHT = 32; - protected static int MIN_HEIGHT = 10; - protected static final int DEFAULT_SELECTED_DAY = -1; - protected static final int DEFAULT_WEEK_START = Time.SUNDAY; - protected static final int DEFAULT_NUM_DAYS = 7; - protected static final int DEFAULT_SHOW_WK_NUM = 0; - protected static final int DEFAULT_FOCUS_MONTH = -1; - - protected static int DAY_SEPARATOR_WIDTH = 1; - - protected static int MINI_DAY_NUMBER_TEXT_SIZE = 14; - protected static int MINI_WK_NUMBER_TEXT_SIZE = 12; - protected static int MINI_TODAY_NUMBER_TEXT_SIZE = 18; - protected static int MINI_TODAY_OUTLINE_WIDTH = 2; - protected static int WEEK_NUM_MARGIN_BOTTOM = 4; - - // used for scaling to the device density - protected static float mScale = 0; - +open class SimpleWeekView(context: Context) : View(context) { // affects the padding on the sides of this view - protected int mPadding = 0; - - protected Rect r = new Rect(); - protected Paint p = new Paint(); - protected Paint mMonthNumPaint; - protected Drawable mSelectedDayLine; + @JvmField protected var mPadding = 0 + @JvmField protected var r: Rect = Rect() + @JvmField protected var p: Paint = Paint() + @JvmField protected var mMonthNumPaint: Paint? = null + @JvmField protected var mSelectedDayLine: Drawable - // Cache the number strings so we don't have to recompute them each time - protected String[] mDayNumbers; - // Quick lookup for checking which days are in the focus month - protected boolean[] mFocusDay; - // Quick lookup for checking which days are in an odd month (to set a different background) - protected boolean[] mOddMonth; // The Julian day of the first day displayed by this item - protected int mFirstJulianDay = -1; + var firstJulianDay = -1 + protected set + // The month of the first day in this week - protected int mFirstMonth = -1; + var firstMonth = -1 + protected set + // The month of the last day in this week - protected int mLastMonth = -1; + var lastMonth = -1 + protected set + // The position of this week, equivalent to weeks since the week of Jan 1st, // 1970 - protected int mWeek = -1; + @JvmField protected var mWeek = -1 + // Quick reference to the width of this view, matches parent - protected int mWidth; + @JvmField protected var mWidth = 0 + // The height this view should draw at in pixels, set by height param - protected int mHeight = DEFAULT_HEIGHT; + @JvmField protected var mHeight = DEFAULT_HEIGHT + // Whether the week number should be shown - protected boolean mShowWeekNum = false; + @JvmField protected var mShowWeekNum = false + // If this view contains the selected day - protected boolean mHasSelectedDay = false; + @JvmField protected var mHasSelectedDay = false + // If this view contains the today - protected boolean mHasToday = false; + @JvmField protected var mHasToday = false + // Which day is selected [0-6] or -1 if no day is selected - protected int mSelectedDay = DEFAULT_SELECTED_DAY; + @JvmField protected var mSelectedDay = DEFAULT_SELECTED_DAY + // Which day is today [0-6] or -1 if no day is today - protected int mToday = DEFAULT_SELECTED_DAY; + @JvmField protected var mToday = DEFAULT_SELECTED_DAY + // Which day of the week to start on [0-6] - protected int mWeekStart = DEFAULT_WEEK_START; + @JvmField protected var mWeekStart = DEFAULT_WEEK_START + // How many days to display - protected int mNumDays = DEFAULT_NUM_DAYS; + @JvmField protected var mNumDays = DEFAULT_NUM_DAYS + // The number of days + a spot for week number if it is displayed - protected int mNumCells = mNumDays; + @JvmField protected var mNumCells = mNumDays + // The left edge of the selected day - protected int mSelectedLeft = -1; + @JvmField protected var mSelectedLeft = -1 + // The right edge of the selected day - protected int mSelectedRight = -1; + @JvmField protected var mSelectedRight = -1 + + // The Julian day of the first day displayed by this item + @JvmField protected var mFirstJulianDay = -1 + // The timezone to display times/dates in (used for determining when Today // is) - protected String mTimeZone = Time.getCurrentTimezone(); - - protected int mBGColor; - protected int mSelectedWeekBGColor; - protected int mFocusMonthColor; - protected int mOtherMonthColor; - protected int mDaySeparatorColor; - protected int mTodayOutlineColor; - protected int mWeekNumColor; - - public SimpleWeekView(Context context) { - super(context); - - Resources res = context.getResources(); - - mBGColor = res.getColor(R.color.month_bgcolor); - mSelectedWeekBGColor = res.getColor(R.color.month_selected_week_bgcolor); - mFocusMonthColor = res.getColor(R.color.month_mini_day_number); - mOtherMonthColor = res.getColor(R.color.month_other_month_day_number); - mDaySeparatorColor = res.getColor(R.color.month_grid_lines); - mTodayOutlineColor = res.getColor(R.color.mini_month_today_outline_color); - mWeekNumColor = res.getColor(R.color.month_week_num_color); - mSelectedDayLine = res.getDrawable(R.drawable.dayline_minical_holo_light); - - if (mScale == 0) { - mScale = context.getResources().getDisplayMetrics().density; - if (mScale != 1) { - DEFAULT_HEIGHT *= mScale; - MIN_HEIGHT *= mScale; - MINI_DAY_NUMBER_TEXT_SIZE *= mScale; - MINI_TODAY_NUMBER_TEXT_SIZE *= mScale; - MINI_TODAY_OUTLINE_WIDTH *= mScale; - WEEK_NUM_MARGIN_BOTTOM *= mScale; - DAY_SEPARATOR_WIDTH *= mScale; - MINI_WK_NUMBER_TEXT_SIZE *= mScale; - } - } + @JvmField protected var mTimeZone: String = Time.getCurrentTimezone() + @JvmField protected var mBGColor: Int + @JvmField protected var mSelectedWeekBGColor: Int + @JvmField protected var mFocusMonthColor: Int + @JvmField protected var mOtherMonthColor: Int + @JvmField protected var mDaySeparatorColor: Int + @JvmField protected var mTodayOutlineColor: Int + @JvmField protected var mWeekNumColor: Int - // Sets up any standard paints that will be used - initView(); - } + // Cache the number strings so we don't have to recompute them each time + @JvmField protected var mDayNumbers: Array = arrayOfNulls(mNumCells) + + // Quick lookup for checking which days are in the focus month + @JvmField protected var mFocusDay: BooleanArray = BooleanArray(mNumCells) + + // Quick lookup for checking which days are in an odd month (to set a different background) + @JvmField protected var mOddMonth: BooleanArray = BooleanArray(mNumCells) /** * Sets all the parameters for displaying this week. The only required * parameter is the week number. Other parameters have a default value and * will only update if a new value is included, except for focus month, * which will always default to no focus month if no value is passed in. See - * {@link #VIEW_PARAMS_HEIGHT} for more info on parameters. + * [.VIEW_PARAMS_HEIGHT] for more info on parameters. * * @param params A map of the new parameters, see - * {@link #VIEW_PARAMS_HEIGHT} + * [.VIEW_PARAMS_HEIGHT] * @param tz The time zone this view should reference times in */ - public void setWeekParams(HashMap params, String tz) { + open fun setWeekParams(params: HashMap, tz: String) { if (!params.containsKey(VIEW_PARAMS_WEEK)) { - throw new InvalidParameterException("You must specify the week number for this view"); + throw InvalidParameterException("You must specify the week number for this view") } - setTag(params); - mTimeZone = tz; + setTag(params) + mTimeZone = tz // We keep the current value for any params not present if (params.containsKey(VIEW_PARAMS_HEIGHT)) { - mHeight = params.get(VIEW_PARAMS_HEIGHT); + mHeight = (params.get(VIEW_PARAMS_HEIGHT) ?: 0) as Int if (mHeight < MIN_HEIGHT) { - mHeight = MIN_HEIGHT; + mHeight = MIN_HEIGHT } } if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) { - mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY); + mSelectedDay = (params.get(VIEW_PARAMS_SELECTED_DAY) ?: 0) as Int } - mHasSelectedDay = mSelectedDay != -1; + mHasSelectedDay = mSelectedDay != -1 if (params.containsKey(VIEW_PARAMS_NUM_DAYS)) { - mNumDays = params.get(VIEW_PARAMS_NUM_DAYS); + mNumDays = (params.get(VIEW_PARAMS_NUM_DAYS) ?: 0) as Int } if (params.containsKey(VIEW_PARAMS_SHOW_WK_NUM)) { - if (params.get(VIEW_PARAMS_SHOW_WK_NUM) != 0) { - mShowWeekNum = true; + mShowWeekNum = if ((params.get(VIEW_PARAMS_SHOW_WK_NUM) ?: 0) as Int != 0) { + true } else { - mShowWeekNum = false; + false } } - mNumCells = mShowWeekNum ? mNumDays + 1 : mNumDays; + mNumCells = if (mShowWeekNum) mNumDays + 1 else mNumDays // Allocate space for caching the day numbers and focus values - mDayNumbers = new String[mNumCells]; - mFocusDay = new boolean[mNumCells]; - mOddMonth = new boolean[mNumCells]; - mWeek = params.get(VIEW_PARAMS_WEEK); - int julianMonday = Utils.getJulianMondayFromWeeksSinceEpoch(mWeek); - Time time = new Time(tz); - time.setJulianDay(julianMonday); + mDayNumbers = arrayOfNulls(mNumCells) + mFocusDay = BooleanArray(mNumCells) + mOddMonth = BooleanArray(mNumCells) + mWeek = (params.get(VIEW_PARAMS_WEEK) ?: 0) as Int + val julianMonday: Int = Utils.getJulianMondayFromWeeksSinceEpoch(mWeek) + val time = Time(tz) + time.setJulianDay(julianMonday) // If we're showing the week number calculate it based on Monday - int i = 0; + var i = 0 if (mShowWeekNum) { - mDayNumbers[0] = Integer.toString(time.getWeekNumber()); - i++; + mDayNumbers[0] = Integer.toString(time.getWeekNumber()) + i++ } - if (params.containsKey(VIEW_PARAMS_WEEK_START)) { - mWeekStart = params.get(VIEW_PARAMS_WEEK_START); + mWeekStart = (params.get(VIEW_PARAMS_WEEK_START) ?: 0) as Int } // Now adjust our starting day based on the start day of the week // If the week is set to start on a Saturday the first week will be // Dec 27th 1969 -Jan 2nd, 1970 - if (time.weekDay != mWeekStart) { - int diff = time.weekDay - mWeekStart; + if (time.weekDay !== mWeekStart) { + var diff: Int = time.weekDay - mWeekStart if (diff < 0) { - diff += 7; + diff += 7 } - time.monthDay -= diff; - time.normalize(true); + time.monthDay -= diff + time.normalize(true) } - - mFirstJulianDay = Time.getJulianDay(time.toMillis(true), time.gmtoff); - mFirstMonth = time.month; + firstJulianDay = Time.getJulianDay(time.toMillis(true), time.gmtoff) + firstMonth = time.month // Figure out what day today is - Time today = new Time(tz); - today.setToNow(); - mHasToday = false; - mToday = -1; - - int focusMonth = params.containsKey(VIEW_PARAMS_FOCUS_MONTH) ? params.get( - VIEW_PARAMS_FOCUS_MONTH) - : DEFAULT_FOCUS_MONTH; - - for (; i < mNumCells; i++) { - if (time.monthDay == 1) { - mFirstMonth = time.month; + val today = Time(tz) + today.setToNow() + mHasToday = false + mToday = -1 + val focusMonth = if (params.containsKey(VIEW_PARAMS_FOCUS_MONTH)) params.get( + VIEW_PARAMS_FOCUS_MONTH) else DEFAULT_FOCUS_MONTH + while (i < mNumCells) { + if (time.monthDay === 1) { + firstMonth = time.month } - mOddMonth [i] = (time.month %2) == 1; - if (time.month == focusMonth) { - mFocusDay[i] = true; + mOddMonth[i] = time.month % 2 === 1 + if (time.month === focusMonth) { + mFocusDay[i] = true } else { - mFocusDay[i] = false; + mFocusDay[i] = false } - if (time.year == today.year && time.yearDay == today.yearDay) { - mHasToday = true; - mToday = i; + if (time.year === today.year && time.yearDay === today.yearDay) { + mHasToday = true + mToday = i } - mDayNumbers[i] = Integer.toString(time.monthDay++); - time.normalize(true); + mDayNumbers[i] = Integer.toString(time.monthDay++) + time.normalize(true) + i++ } // We do one extra add at the end of the loop, if that pushed us to a // new month undo it - if (time.monthDay == 1) { - time.monthDay--; - time.normalize(true); + if (time.monthDay === 1) { + time.monthDay-- + time.normalize(true) } - mLastMonth = time.month; - - updateSelectionPositions(); + lastMonth = time.month + updateSelectionPositions() } /** * Sets up the text and style properties for painting. Override this if you * want to use a different paint. */ - protected void initView() { - p.setFakeBoldText(false); - p.setAntiAlias(true); - p.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); - p.setStyle(Style.FILL); - - mMonthNumPaint = new Paint(); - mMonthNumPaint.setFakeBoldText(true); - mMonthNumPaint.setAntiAlias(true); - mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); - mMonthNumPaint.setColor(mFocusMonthColor); - mMonthNumPaint.setStyle(Style.FILL); - mMonthNumPaint.setTextAlign(Align.CENTER); - } - - /** - * Returns the month of the first day in this week - * - * @return The month the first day of this view is in - */ - public int getFirstMonth() { - return mFirstMonth; - } - - /** - * Returns the month of the last day in this week - * - * @return The month the last day of this view is in - */ - public int getLastMonth() { - return mLastMonth; - } - - /** - * Returns the julian day of the first day in this view. - * - * @return The julian day of the first day in the view. - */ - public int getFirstJulianDay() { - return mFirstJulianDay; + open protected fun initView() { + p.setFakeBoldText(false) + p.setAntiAlias(true) + p.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE as Float) + p.setStyle(Style.FILL) + mMonthNumPaint = Paint() + mMonthNumPaint?.setFakeBoldText(true) + mMonthNumPaint?.setAntiAlias(true) + mMonthNumPaint?.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE as Float) + mMonthNumPaint?.setColor(mFocusMonthColor) + mMonthNumPaint?.setStyle(Style.FILL) + mMonthNumPaint?.setTextAlign(Align.CENTER) } /** @@ -363,38 +259,37 @@ public class SimpleWeekView extends View { * * @param x The x position of the touch event * @return A time object for the tapped day or null if the position wasn't - * in a day + * in a day */ - public Time getDayFromLocation(float x) { - int dayStart = mShowWeekNum ? (mWidth - mPadding * 2) / mNumCells + mPadding : mPadding; + open fun getDayFromLocation(x: Float): Time? { + val dayStart = if (mShowWeekNum) (mWidth - mPadding * 2) / mNumCells + mPadding + else mPadding if (x < dayStart || x > mWidth - mPadding) { - return null; + return null } // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels - int dayPosition = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)); - int day = mFirstJulianDay + dayPosition; - - Time time = new Time(mTimeZone); + val dayPosition = ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)).toInt() + var day = firstJulianDay + dayPosition + val time = Time(mTimeZone) if (mWeek == 0) { // This week is weird... if (day < Time.EPOCH_JULIAN_DAY) { - day++; + day++ } else if (day == Time.EPOCH_JULIAN_DAY) { - time.set(1, 0, 1970); - time.normalize(true); - return time; + time.set(1, 0, 1970) + time.normalize(true) + return time } } - - time.setJulianDay(day); - return time; + time.setJulianDay(day) + return time } @Override - protected void onDraw(Canvas canvas) { - drawBackground(canvas); - drawWeekNums(canvas); - drawDaySeparators(canvas); + override protected fun onDraw(canvas: Canvas) { + drawBackground(canvas) + drawWeekNums(canvas) + drawDaySeparators(canvas) } /** @@ -403,21 +298,21 @@ public class SimpleWeekView extends View { * * @param canvas The canvas to draw on */ - protected void drawBackground(Canvas canvas) { + open protected fun drawBackground(canvas: Canvas) { if (mHasSelectedDay) { - p.setColor(mSelectedWeekBGColor); - p.setStyle(Style.FILL); + p.setColor(mSelectedWeekBGColor) + p.setStyle(Style.FILL) } else { - return; + return } - r.top = 1; - r.bottom = mHeight - 1; - r.left = mPadding; - r.right = mSelectedLeft; - canvas.drawRect(r, p); - r.left = mSelectedRight; - r.right = mWidth - mPadding; - canvas.drawRect(r, p); + r.top = 1 + r.bottom = mHeight - 1 + r.left = mPadding + r.right = mSelectedLeft + canvas.drawRect(r, p) + r.left = mSelectedRight + r.right = mWidth - mPadding + canvas.drawRect(r, p) } /** @@ -426,41 +321,40 @@ public class SimpleWeekView extends View { * * @param canvas The canvas to draw on */ - protected void drawWeekNums(Canvas canvas) { - int y = ((mHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2) - DAY_SEPARATOR_WIDTH; - int nDays = mNumCells; - - int i = 0; - int divisor = 2 * nDays; + open protected fun drawWeekNums(canvas: Canvas) { + val y = (mHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2 - DAY_SEPARATOR_WIDTH + val nDays = mNumCells + var i = 0 + val divisor = 2 * nDays if (mShowWeekNum) { - p.setTextSize(MINI_WK_NUMBER_TEXT_SIZE); - p.setStyle(Style.FILL); - p.setTextAlign(Align.CENTER); - p.setAntiAlias(true); - p.setColor(mWeekNumColor); - int x = (mWidth - mPadding * 2) / divisor + mPadding; - canvas.drawText(mDayNumbers[0], x, y, p); - i++; + p.setTextSize(MINI_WK_NUMBER_TEXT_SIZE as Float) + p.setStyle(Style.FILL) + p.setTextAlign(Align.CENTER) + p.setAntiAlias(true) + p.setColor(mWeekNumColor) + val x = (mWidth - mPadding * 2) / divisor + mPadding + canvas.drawText(mDayNumbers[0] ?: "", x as Float, y as Float, p) + i++ } - - boolean isFocusMonth = mFocusDay[i]; - mMonthNumPaint.setColor(isFocusMonth ? mFocusMonthColor : mOtherMonthColor); - mMonthNumPaint.setFakeBoldText(false); - for (; i < nDays; i++) { + var isFocusMonth = mFocusDay[i] + mMonthNumPaint?.setColor(if (isFocusMonth) mFocusMonthColor else mOtherMonthColor) + mMonthNumPaint?.setFakeBoldText(false) + while (i < nDays) { if (mFocusDay[i] != isFocusMonth) { - isFocusMonth = mFocusDay[i]; - mMonthNumPaint.setColor(isFocusMonth ? mFocusMonthColor : mOtherMonthColor); + isFocusMonth = mFocusDay[i] + mMonthNumPaint?.setColor(if (isFocusMonth) mFocusMonthColor else mOtherMonthColor) } if (mHasToday && mToday == i) { - mMonthNumPaint.setTextSize(MINI_TODAY_NUMBER_TEXT_SIZE); - mMonthNumPaint.setFakeBoldText(true); + mMonthNumPaint?.setTextSize(MINI_TODAY_NUMBER_TEXT_SIZE as Float) + mMonthNumPaint?.setFakeBoldText(true) } - int x = (2 * i + 1) * (mWidth - mPadding * 2) / (divisor) + mPadding; - canvas.drawText(mDayNumbers[i], x, y, mMonthNumPaint); + val x = (2 * i + 1) * (mWidth - mPadding * 2) / divisor + mPadding + canvas.drawText(mDayNumbers[i] ?: "", x as Float, y as Float, mMonthNumPaint ?: Paint()) if (mHasToday && mToday == i) { - mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); - mMonthNumPaint.setFakeBoldText(false); + mMonthNumPaint?.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE as Float) + mMonthNumPaint?.setFakeBoldText(false) } + i++ } } @@ -470,82 +364,173 @@ public class SimpleWeekView extends View { * * @param canvas The canvas to draw on */ - protected void drawDaySeparators(Canvas canvas) { + open protected fun drawDaySeparators(canvas: Canvas) { if (mHasSelectedDay) { - r.top = 1; - r.bottom = mHeight - 1; - r.left = mSelectedLeft + 1; - r.right = mSelectedRight - 1; - p.setStrokeWidth(MINI_TODAY_OUTLINE_WIDTH); - p.setStyle(Style.STROKE); - p.setColor(mTodayOutlineColor); - canvas.drawRect(r, p); + r.top = 1 + r.bottom = mHeight - 1 + r.left = mSelectedLeft + 1 + r.right = mSelectedRight - 1 + p.setStrokeWidth(MINI_TODAY_OUTLINE_WIDTH as Float) + p.setStyle(Style.STROKE) + p.setColor(mTodayOutlineColor) + canvas.drawRect(r, p) } if (mShowWeekNum) { - p.setColor(mDaySeparatorColor); - p.setStrokeWidth(DAY_SEPARATOR_WIDTH); - - int x = (mWidth - mPadding * 2) / mNumCells + mPadding; - canvas.drawLine(x, 0, x, mHeight, p); + p.setColor(mDaySeparatorColor) + p.setStrokeWidth(DAY_SEPARATOR_WIDTH as Float) + val x = (mWidth - mPadding * 2) / mNumCells + mPadding + canvas.drawLine(x as Float, 0 as Float, x as Float, mHeight as Float, p) } } @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - mWidth = w; - updateSelectionPositions(); + override protected fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + mWidth = w + updateSelectionPositions() } /** * This calculates the positions for the selected day lines. */ - protected void updateSelectionPositions() { + open protected fun updateSelectionPositions() { if (mHasSelectedDay) { - int selectedPosition = mSelectedDay - mWeekStart; + var selectedPosition = mSelectedDay - mWeekStart if (selectedPosition < 0) { - selectedPosition += 7; + selectedPosition += 7 } if (mShowWeekNum) { - selectedPosition++; + selectedPosition++ } - mSelectedLeft = selectedPosition * (mWidth - mPadding * 2) / mNumCells - + mPadding; - mSelectedRight = (selectedPosition + 1) * (mWidth - mPadding * 2) / mNumCells - + mPadding; + mSelectedLeft = (selectedPosition * (mWidth - mPadding * 2) / mNumCells + + mPadding) + mSelectedRight = ((selectedPosition + 1) * (mWidth - mPadding * 2) / mNumCells + + mPadding) } } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight); + override protected fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight) } @Override - public boolean onHoverEvent(MotionEvent event) { - Context context = getContext(); + override fun onHoverEvent(event: MotionEvent): Boolean { + val context: Context = getContext() // only send accessibility events if accessibility and exploration are // on. - AccessibilityManager am = (AccessibilityManager) context - .getSystemService(Service.ACCESSIBILITY_SERVICE); + val am: AccessibilityManager = context + .getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager if (!am.isEnabled() || !am.isTouchExplorationEnabled()) { - return super.onHoverEvent(event); + return super.onHoverEvent(event) } - if (event.getAction() != MotionEvent.ACTION_HOVER_EXIT) { - Time hover = getDayFromLocation(event.getX()); + if (event.getAction() !== MotionEvent.ACTION_HOVER_EXIT) { + val hover: Time? = getDayFromLocation(event.getX()) if (hover != null - && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) != 0)) { - Long millis = hover.toMillis(true); - String date = Utils.formatDateRange(context, millis, millis, - DateUtils.FORMAT_SHOW_DATE); - AccessibilityEvent accessEvent = - AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); - accessEvent.getText().add(date); - sendAccessibilityEventUnchecked(accessEvent); - mLastHoverTime = hover; + && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) !== 0)) { + val millis: Long = hover.toMillis(true) + val date: String = Utils.formatDateRange(context, millis, millis, + DateUtils.FORMAT_SHOW_DATE) + val accessEvent: AccessibilityEvent = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) + accessEvent.getText().add(date) + sendAccessibilityEventUnchecked(accessEvent) + mLastHoverTime = hover } } - return true; + return true + } + + @JvmField var mLastHoverTime: Time? = null + + companion object { + private const val TAG = "MonthView" + /** + * These params can be passed into the view to control how it appears. + * [.VIEW_PARAMS_WEEK] is the only required field, though the default + * values are unlikely to fit most layouts correctly. + */ + /** + * This sets the height of this week in pixels + */ + const val VIEW_PARAMS_HEIGHT = "height" + + /** + * This specifies the position (or weeks since the epoch) of this week, + * calculated using [Utils.getWeeksSinceEpochFromJulianDay] + */ + const val VIEW_PARAMS_WEEK = "week" + + /** + * This sets one of the days in this view as selected [Time.SUNDAY] + * through [Time.SATURDAY]. + */ + const val VIEW_PARAMS_SELECTED_DAY = "selected_day" + + /** + * Which day the week should start on. [Time.SUNDAY] through + * [Time.SATURDAY]. + */ + const val VIEW_PARAMS_WEEK_START = "week_start" + + /** + * How many days to display at a time. Days will be displayed starting with + * [.mWeekStart]. + */ + const val VIEW_PARAMS_NUM_DAYS = "num_days" + + /** + * Which month is currently in focus, as defined by [Time.month] + * [0-11]. + */ + const val VIEW_PARAMS_FOCUS_MONTH = "focus_month" + + /** + * If this month should display week numbers. false if 0, true otherwise. + */ + const val VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num" + protected var DEFAULT_HEIGHT = 32 + protected var MIN_HEIGHT = 10 + protected const val DEFAULT_SELECTED_DAY = -1 + protected val DEFAULT_WEEK_START: Int = Time.SUNDAY + protected const val DEFAULT_NUM_DAYS = 7 + protected const val DEFAULT_SHOW_WK_NUM = 0 + protected const val DEFAULT_FOCUS_MONTH = -1 + protected var DAY_SEPARATOR_WIDTH = 1 + protected var MINI_DAY_NUMBER_TEXT_SIZE = 14 + protected var MINI_WK_NUMBER_TEXT_SIZE = 12 + protected var MINI_TODAY_NUMBER_TEXT_SIZE = 18 + protected var MINI_TODAY_OUTLINE_WIDTH = 2 + protected var WEEK_NUM_MARGIN_BOTTOM = 4 + + // used for scaling to the device density + @JvmField protected var mScale = 0f } - Time mLastHoverTime = null; + init { + val res: Resources = context.getResources() + mBGColor = res.getColor(R.color.month_bgcolor) + mSelectedWeekBGColor = res.getColor(R.color.month_selected_week_bgcolor) + mFocusMonthColor = res.getColor(R.color.month_mini_day_number) + mOtherMonthColor = res.getColor(R.color.month_other_month_day_number) + mDaySeparatorColor = res.getColor(R.color.month_grid_lines) + mTodayOutlineColor = res.getColor(R.color.mini_month_today_outline_color) + mWeekNumColor = res.getColor(R.color.month_week_num_color) + mSelectedDayLine = res.getDrawable(R.drawable.dayline_minical_holo_light) + if (mScale == 0f) { + mScale = context.getResources().getDisplayMetrics().density + if (mScale != 1f) { + DEFAULT_HEIGHT *= mScale.toInt() + MIN_HEIGHT *= mScale.toInt() + MINI_DAY_NUMBER_TEXT_SIZE *= mScale.toInt() + MINI_TODAY_NUMBER_TEXT_SIZE *= mScale.toInt() + MINI_TODAY_OUTLINE_WIDTH *= mScale.toInt() + WEEK_NUM_MARGIN_BOTTOM *= mScale.toInt() + DAY_SEPARATOR_WIDTH *= mScale.toInt() + MINI_WK_NUMBER_TEXT_SIZE *= mScale.toInt() + } + } + + // Sets up any standard paints that will be used + initView() + } } \ No newline at end of file -- cgit v1.2.3 From c46c8a50dcc83a619d85b02d12462a61d81336d4 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 24 Jun 2021 15:56:14 +0000 Subject: AOSP/Calendar - Copy of Utils.java The Java code in Event.java has been copied into a corresponding .kt file. Test: manual - opening both .java and .kt files shows that they are identical. Change-Id: I4ee70efb45490cf9ffae1926fbf8ae4941377fb9 --- src/com/android/calendar/Utils.kt | 1499 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1499 insertions(+) create mode 100644 src/com/android/calendar/Utils.kt diff --git a/src/com/android/calendar/Utils.kt b/src/com/android/calendar/Utils.kt new file mode 100644 index 00000000..cc55c999 --- /dev/null +++ b/src/com/android/calendar/Utils.kt @@ -0,0 +1,1499 @@ +/* + * Copyright (C) 2006 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 static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; + +import android.accounts.Account; +import android.app.Activity; +import android.app.SearchManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.provider.CalendarContract.Calendars; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.text.style.URLSpan; +import android.text.util.Linkify; +import android.util.Log; + +import com.android.calendar.CalendarController.ViewType; +import com.android.calendar.CalendarUtils.TimeZoneUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Formatter; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Utils { + private static final boolean DEBUG = false; + private static final String TAG = "CalUtils"; + + // Set to 0 until we have UI to perform undo + public static final long UNDO_DELAY = 0; + + // For recurring events which instances of the series are being modified + public static final int MODIFY_UNINITIALIZED = 0; + public static final int MODIFY_SELECTED = 1; + public static final int MODIFY_ALL_FOLLOWING = 2; + public static final int MODIFY_ALL = 3; + + // When the edit event view finishes it passes back the appropriate exit + // code. + public static final int DONE_REVERT = 1 << 0; + public static final int DONE_SAVE = 1 << 1; + public static final int DONE_DELETE = 1 << 2; + // And should re run with DONE_EXIT if it should also leave the view, just + // exiting is identical to reverting + public static final int DONE_EXIT = 1 << 0; + + public static final String OPEN_EMAIL_MARKER = " <"; + public static final String CLOSE_EMAIL_MARKER = ">"; + + public static final String INTENT_KEY_DETAIL_VIEW = "DETAIL_VIEW"; + public static final String INTENT_KEY_VIEW_TYPE = "VIEW"; + public static final String INTENT_VALUE_VIEW_TYPE_DAY = "DAY"; + public static final String INTENT_KEY_HOME = "KEY_HOME"; + + public static final int MONDAY_BEFORE_JULIAN_EPOCH = Time.EPOCH_JULIAN_DAY - 3; + public static final int DECLINED_EVENT_ALPHA = 0x66; + public static final int DECLINED_EVENT_TEXT_ALPHA = 0xC0; + + private static final float SATURATION_ADJUST = 1.3f; + private static final float INTENSITY_ADJUST = 0.8f; + + // Defines used by the DNA generation code + static final int DAY_IN_MINUTES = 60 * 24; + static final int WEEK_IN_MINUTES = DAY_IN_MINUTES * 7; + // The work day is being counted as 6am to 8pm + static int WORK_DAY_MINUTES = 14 * 60; + static int WORK_DAY_START_MINUTES = 6 * 60; + static int WORK_DAY_END_MINUTES = 20 * 60; + static int WORK_DAY_END_LENGTH = (24 * 60) - WORK_DAY_END_MINUTES; + static int CONFLICT_COLOR = 0xFF000000; + static boolean mMinutesLoaded = false; + + public static final int YEAR_MIN = 1970; + public static final int YEAR_MAX = 2036; + + // The name of the shared preferences file. This name must be maintained for + // historical + // reasons, as it's what PreferenceManager assigned the first time the file + // was created. + static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; + + public static final String KEY_QUICK_RESPONSES = "preferences_quick_responses"; + + public static final String KEY_ALERTS_VIBRATE_WHEN = "preferences_alerts_vibrateWhen"; + + public static final String APPWIDGET_DATA_TYPE = "vnd.android.data/update"; + + static final String MACHINE_GENERATED_ADDRESS = "calendar.google.com"; + + private static final TimeZoneUtils mTZUtils = new TimeZoneUtils(SHARED_PREFS_NAME); + private static boolean mAllowWeekForDetailView = false; + private static long mTardis = 0; + private static String sVersion = null; + + private static final Pattern mWildcardPattern = Pattern.compile("^.*$"); + + /** + * A coordinate must be of the following form for Google Maps to correctly use it: + * Latitude, Longitude + * + * This may be in decimal form: + * Latitude: {-90 to 90} + * Longitude: {-180 to 180} + * + * Or, in degrees, minutes, and seconds: + * Latitude: {-90 to 90}° {0 to 59}' {0 to 59}" + * Latitude: {-180 to 180}° {0 to 59}' {0 to 59}" + * + or - degrees may also be represented with N or n, S or s for latitude, and with + * E or e, W or w for longitude, where the direction may either precede or follow the value. + * + * Some examples of coordinates that will be accepted by the regex: + * 37.422081°, -122.084576° + * 37.422081,-122.084576 + * +37°25'19.49", -122°5'4.47" + * 37°25'19.49"N, 122°5'4.47"W + * N 37° 25' 19.49", W 122° 5' 4.47" + **/ + private static final String COORD_DEGREES_LATITUDE = + "([-+NnSs]" + "(\\s)*)?" + + "[1-9]?[0-9](\u00B0)" + "(\\s)*" + + "([1-5]?[0-9]\')?" + "(\\s)*" + + "([1-5]?[0-9]" + "(\\.[0-9]+)?\")?" + + "((\\s)*" + "[NnSs])?"; + private static final String COORD_DEGREES_LONGITUDE = + "([-+EeWw]" + "(\\s)*)?" + + "(1)?[0-9]?[0-9](\u00B0)" + "(\\s)*" + + "([1-5]?[0-9]\')?" + "(\\s)*" + + "([1-5]?[0-9]" + "(\\.[0-9]+)?\")?" + + "((\\s)*" + "[EeWw])?"; + private static final String COORD_DEGREES_PATTERN = + COORD_DEGREES_LATITUDE + + "(\\s)*" + "," + "(\\s)*" + + COORD_DEGREES_LONGITUDE; + private static final String COORD_DECIMAL_LATITUDE = + "[+-]?" + + "[1-9]?[0-9]" + "(\\.[0-9]+)" + + "(\u00B0)?"; + private static final String COORD_DECIMAL_LONGITUDE = + "[+-]?" + + "(1)?[0-9]?[0-9]" + "(\\.[0-9]+)" + + "(\u00B0)?"; + private static final String COORD_DECIMAL_PATTERN = + COORD_DECIMAL_LATITUDE + + "(\\s)*" + "," + "(\\s)*" + + COORD_DECIMAL_LONGITUDE; + private static final Pattern COORD_PATTERN = + Pattern.compile(COORD_DEGREES_PATTERN + "|" + COORD_DECIMAL_PATTERN); + + private static final String NANP_ALLOWED_SYMBOLS = "()+-*#."; + private static final int NANP_MIN_DIGITS = 7; + private static final int NANP_MAX_DIGITS = 11; + + + /** + * Returns whether the SDK is the Jellybean release or later. + */ + public static boolean isJellybeanOrLater() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; + } + + /** + * Returns whether the SDK is the KeyLimePie release or later. + */ + public static boolean isKeyLimePieOrLater() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + } + + public static int getViewTypeFromIntentAndSharedPref(Activity activity) { + Intent intent = activity.getIntent(); + Bundle extras = intent.getExtras(); + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(activity); + + if (TextUtils.equals(intent.getAction(), Intent.ACTION_EDIT)) { + return ViewType.EDIT; + } + if (extras != null) { + if (extras.getBoolean(INTENT_KEY_DETAIL_VIEW, false)) { + // This is the "detail" view which is either agenda or day view + return prefs.getInt(GeneralPreferences.KEY_DETAILED_VIEW, + GeneralPreferences.DEFAULT_DETAILED_VIEW); + } else if (INTENT_VALUE_VIEW_TYPE_DAY.equals(extras.getString(INTENT_KEY_VIEW_TYPE))) { + // Not sure who uses this. This logic came from LaunchActivity + return ViewType.DAY; + } + } + + // Default to the last view + return prefs.getInt( + GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW); + } + + /** + * Gets the intent action for telling the widget to update. + */ + public static String getWidgetUpdateAction(Context context) { + return context.getPackageName() + ".APPWIDGET_UPDATE"; + } + + /** + * Gets the intent action for telling the widget to update. + */ + public static String getWidgetScheduledUpdateAction(Context context) { + return context.getPackageName() + ".APPWIDGET_SCHEDULED_UPDATE"; + } + + /** + * Writes a new home time zone to the db. Updates the home time zone in the + * db asynchronously and updates the local cache. Sending a time zone of + * **tbd** will cause it to be set to the device's time zone. null or empty + * tz will be ignored. + * + * @param context The calling activity + * @param timeZone The time zone to set Calendar to, or **tbd** + */ + public static void setTimeZone(Context context, String timeZone) { + mTZUtils.setTimeZone(context, timeZone); + } + + /** + * Gets the time zone that Calendar should be displayed in This is a helper + * method to get the appropriate time zone for Calendar. If this is the + * first time this method has been called it will initiate an asynchronous + * query to verify that the data in preferences is correct. The callback + * supplied will only be called if this query returns a value other than + * what is stored in preferences and should cause the calling activity to + * refresh anything that depends on calling this method. + * + * @param context The calling activity + * @param callback The runnable that should execute if a query returns new + * values + * @return The string value representing the time zone Calendar should + * display + */ + public static String getTimeZone(Context context, Runnable callback) { + return mTZUtils.getTimeZone(context, callback); + } + + /** + * Formats a date or a time range according to the local conventions. + * + * @param context the context is required only if the time is shown + * @param startMillis the start time in UTC milliseconds + * @param endMillis the end time in UTC milliseconds + * @param flags a bit mask of options See {@link DateUtils#formatDateRange(Context, Formatter, + * long, long, int, String) formatDateRange} + * @return a string containing the formatted date/time range. + */ + public static String formatDateRange( + Context context, long startMillis, long endMillis, int flags) { + return mTZUtils.formatDateRange(context, startMillis, endMillis, flags); + } + + public static boolean getDefaultVibrate(Context context, SharedPreferences prefs) { + boolean vibrate; + if (prefs.contains(KEY_ALERTS_VIBRATE_WHEN)) { + // Migrate setting to new 4.2 behavior + // + // silent and never -> off + // always -> on + String vibrateWhen = prefs.getString(KEY_ALERTS_VIBRATE_WHEN, null); + vibrate = vibrateWhen != null && vibrateWhen.equals(context + .getString(R.string.prefDefault_alerts_vibrate_true)); + prefs.edit().remove(KEY_ALERTS_VIBRATE_WHEN).commit(); + Log.d(TAG, "Migrating KEY_ALERTS_VIBRATE_WHEN(" + vibrateWhen + + ") to KEY_ALERTS_VIBRATE = " + vibrate); + } else { + vibrate = prefs.getBoolean(GeneralPreferences.KEY_ALERTS_VIBRATE, + false); + } + return vibrate; + } + + public static String[] getSharedPreference(Context context, String key, String[] defaultValue) { + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + Set ss = prefs.getStringSet(key, null); + if (ss != null) { + String strings[] = new String[ss.size()]; + return ss.toArray(strings); + } + return defaultValue; + } + + public static String getSharedPreference(Context context, String key, String defaultValue) { + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + return prefs.getString(key, defaultValue); + } + + public static int getSharedPreference(Context context, String key, int defaultValue) { + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + return prefs.getInt(key, defaultValue); + } + + public static boolean getSharedPreference(Context context, String key, boolean defaultValue) { + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + return prefs.getBoolean(key, defaultValue); + } + + /** + * Asynchronously sets the preference with the given key to the given value + * + * @param context the context to use to get preferences from + * @param key the key of the preference to set + * @param value the value to set + */ + public static void setSharedPreference(Context context, String key, String value) { + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + prefs.edit().putString(key, value).apply(); + } + + public static void setSharedPreference(Context context, String key, String[] values) { + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + LinkedHashSet set = new LinkedHashSet(); + for (String value : values) { + set.add(value); + } + prefs.edit().putStringSet(key, set).apply(); + } + + protected static void tardis() { + mTardis = System.currentTimeMillis(); + } + + protected static long getTardis() { + return mTardis; + } + + public static void setSharedPreference(Context context, String key, boolean value) { + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(key, value); + editor.apply(); + } + + static void setSharedPreference(Context context, String key, int value) { + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + SharedPreferences.Editor editor = prefs.edit(); + editor.putInt(key, value); + editor.apply(); + } + + public static void removeSharedPreference(Context context, String key) { + SharedPreferences prefs = context.getSharedPreferences( + GeneralPreferences.SHARED_PREFS_NAME, Context.MODE_PRIVATE); + prefs.edit().remove(key).apply(); + } + + /** + * Save default agenda/day/week/month view for next time + * + * @param context + * @param viewId {@link CalendarController.ViewType} + */ + static void setDefaultView(Context context, int viewId) { + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + SharedPreferences.Editor editor = prefs.edit(); + + boolean validDetailView = false; + if (mAllowWeekForDetailView && viewId == CalendarController.ViewType.WEEK) { + validDetailView = true; + } else { + validDetailView = viewId == CalendarController.ViewType.AGENDA + || viewId == CalendarController.ViewType.DAY; + } + + if (validDetailView) { + // Record the detail start view + editor.putInt(GeneralPreferences.KEY_DETAILED_VIEW, viewId); + } + + // Record the (new) start view + editor.putInt(GeneralPreferences.KEY_START_VIEW, viewId); + editor.apply(); + } + + public static MatrixCursor matrixCursorFromCursor(Cursor cursor) { + if (cursor == null) { + return null; + } + + String[] columnNames = cursor.getColumnNames(); + if (columnNames == null) { + columnNames = new String[] {}; + } + MatrixCursor newCursor = new MatrixCursor(columnNames); + int numColumns = cursor.getColumnCount(); + String data[] = new String[numColumns]; + cursor.moveToPosition(-1); + while (cursor.moveToNext()) { + for (int i = 0; i < numColumns; i++) { + data[i] = cursor.getString(i); + } + newCursor.addRow(data); + } + return newCursor; + } + + /** + * Compares two cursors to see if they contain the same data. + * + * @return Returns true of the cursors contain the same data and are not + * null, false otherwise + */ + public static boolean compareCursors(Cursor c1, Cursor c2) { + if (c1 == null || c2 == null) { + return false; + } + + int numColumns = c1.getColumnCount(); + if (numColumns != c2.getColumnCount()) { + return false; + } + + if (c1.getCount() != c2.getCount()) { + return false; + } + + c1.moveToPosition(-1); + c2.moveToPosition(-1); + while (c1.moveToNext() && c2.moveToNext()) { + for (int i = 0; i < numColumns; i++) { + if (!TextUtils.equals(c1.getString(i), c2.getString(i))) { + return false; + } + } + } + + return true; + } + + /** + * If the given intent specifies a time (in milliseconds since the epoch), + * then that time is returned. Otherwise, the current time is returned. + */ + public static final long timeFromIntentInMillis(Intent intent) { + // If the time was specified, then use that. Otherwise, use the current + // time. + Uri data = intent.getData(); + long millis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1); + if (millis == -1 && data != null && data.isHierarchical()) { + List path = data.getPathSegments(); + if (path.size() == 2 && path.get(0).equals("time")) { + try { + millis = Long.valueOf(data.getLastPathSegment()); + } catch (NumberFormatException e) { + Log.i("Calendar", "timeFromIntentInMillis: Data existed but no valid time " + + "found. Using current time."); + } + } + } + if (millis <= 0) { + millis = System.currentTimeMillis(); + } + return millis; + } + + /** + * Formats the given Time object so that it gives the month and year (for + * example, "September 2007"). + * + * @param time the time to format + * @return the string containing the weekday and the date + */ + public static String formatMonthYear(Context context, Time time) { + int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY + | DateUtils.FORMAT_SHOW_YEAR; + long millis = time.toMillis(true); + return formatDateRange(context, millis, millis, flags); + } + + /** + * Returns a list joined together by the provided delimiter, for example, + * ["a", "b", "c"] could be joined into "a,b,c" + * + * @param things the things to join together + * @param delim the delimiter to use + * @return a string contained the things joined together + */ + public static String join(List things, String delim) { + StringBuilder builder = new StringBuilder(); + boolean first = true; + for (Object thing : things) { + if (first) { + first = false; + } else { + builder.append(delim); + } + builder.append(thing.toString()); + } + return builder.toString(); + } + + /** + * Returns the week since {@link Time#EPOCH_JULIAN_DAY} (Jan 1, 1970) + * adjusted for first day of week. + * + * This takes a julian day and the week start day and calculates which + * week since {@link Time#EPOCH_JULIAN_DAY} that day occurs in, starting + * at 0. *Do not* use this to compute the ISO week number for the year. + * + * @param julianDay The julian day to calculate the week number for + * @param firstDayOfWeek Which week day is the first day of the week, + * see {@link Time#SUNDAY} + * @return Weeks since the epoch + */ + public static int getWeeksSinceEpochFromJulianDay(int julianDay, int firstDayOfWeek) { + int diff = Time.THURSDAY - firstDayOfWeek; + if (diff < 0) { + diff += 7; + } + int refDay = Time.EPOCH_JULIAN_DAY - diff; + return (julianDay - refDay) / 7; + } + + /** + * Takes a number of weeks since the epoch and calculates the Julian day of + * the Monday for that week. + * + * This assumes that the week containing the {@link Time#EPOCH_JULIAN_DAY} + * is considered week 0. It returns the Julian day for the Monday + * {@code week} weeks after the Monday of the week containing the epoch. + * + * @param week Number of weeks since the epoch + * @return The julian day for the Monday of the given week since the epoch + */ + public static int getJulianMondayFromWeeksSinceEpoch(int week) { + return MONDAY_BEFORE_JULIAN_EPOCH + week * 7; + } + + /** + * Get first day of week as android.text.format.Time constant. + * + * @return the first day of week in android.text.format.Time + */ + public static int getFirstDayOfWeek(Context context) { + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + String pref = prefs.getString( + GeneralPreferences.KEY_WEEK_START_DAY, GeneralPreferences.WEEK_START_DEFAULT); + + int startDay; + if (GeneralPreferences.WEEK_START_DEFAULT.equals(pref)) { + startDay = Calendar.getInstance().getFirstDayOfWeek(); + } else { + startDay = Integer.parseInt(pref); + } + + if (startDay == Calendar.SATURDAY) { + return Time.SATURDAY; + } else if (startDay == Calendar.MONDAY) { + return Time.MONDAY; + } else { + return Time.SUNDAY; + } + } + + /** + * Get first day of week as java.util.Calendar constant. + * + * @return the first day of week as a java.util.Calendar constant + */ + public static int getFirstDayOfWeekAsCalendar(Context context) { + return convertDayOfWeekFromTimeToCalendar(getFirstDayOfWeek(context)); + } + + /** + * Converts the day of the week from android.text.format.Time to java.util.Calendar + */ + public static int convertDayOfWeekFromTimeToCalendar(int timeDayOfWeek) { + switch (timeDayOfWeek) { + case Time.MONDAY: + return Calendar.MONDAY; + case Time.TUESDAY: + return Calendar.TUESDAY; + case Time.WEDNESDAY: + return Calendar.WEDNESDAY; + case Time.THURSDAY: + return Calendar.THURSDAY; + case Time.FRIDAY: + return Calendar.FRIDAY; + case Time.SATURDAY: + return Calendar.SATURDAY; + case Time.SUNDAY: + return Calendar.SUNDAY; + default: + throw new IllegalArgumentException("Argument must be between Time.SUNDAY and " + + "Time.SATURDAY"); + } + } + + /** + * @return true when week number should be shown. + */ + public static boolean getShowWeekNumber(Context context) { + final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + return prefs.getBoolean( + GeneralPreferences.KEY_SHOW_WEEK_NUM, GeneralPreferences.DEFAULT_SHOW_WEEK_NUM); + } + + /** + * @return true when declined events should be hidden. + */ + public static boolean getHideDeclinedEvents(Context context) { + final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + return prefs.getBoolean(GeneralPreferences.KEY_HIDE_DECLINED, false); + } + + public static int getDaysPerWeek(Context context) { + final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); + return prefs.getInt(GeneralPreferences.KEY_DAYS_PER_WEEK, 7); + } + + /** + * Determine whether the column position is Saturday or not. + * + * @param column the column position + * @param firstDayOfWeek the first day of week in android.text.format.Time + * @return true if the column is Saturday position + */ + public static boolean isSaturday(int column, int firstDayOfWeek) { + return (firstDayOfWeek == Time.SUNDAY && column == 6) + || (firstDayOfWeek == Time.MONDAY && column == 5) + || (firstDayOfWeek == Time.SATURDAY && column == 0); + } + + /** + * Determine whether the column position is Sunday or not. + * + * @param column the column position + * @param firstDayOfWeek the first day of week in android.text.format.Time + * @return true if the column is Sunday position + */ + public static boolean isSunday(int column, int firstDayOfWeek) { + return (firstDayOfWeek == Time.SUNDAY && column == 0) + || (firstDayOfWeek == Time.MONDAY && column == 6) + || (firstDayOfWeek == Time.SATURDAY && column == 1); + } + + /** + * Convert given UTC time into current local time. This assumes it is for an + * allday event and will adjust the time to be on a midnight boundary. + * + * @param recycle Time object to recycle, otherwise null. + * @param utcTime Time to convert, in UTC. + * @param tz The time zone to convert this time to. + */ + public static long convertAlldayUtcToLocal(Time recycle, long utcTime, String tz) { + if (recycle == null) { + recycle = new Time(); + } + recycle.timezone = Time.TIMEZONE_UTC; + recycle.set(utcTime); + recycle.timezone = tz; + return recycle.normalize(true); + } + + public static long convertAlldayLocalToUTC(Time recycle, long localTime, String tz) { + if (recycle == null) { + recycle = new Time(); + } + recycle.timezone = tz; + recycle.set(localTime); + recycle.timezone = Time.TIMEZONE_UTC; + return recycle.normalize(true); + } + + /** + * Finds and returns the next midnight after "theTime" in milliseconds UTC + * + * @param recycle - Time object to recycle, otherwise null. + * @param theTime - Time used for calculations (in UTC) + * @param tz The time zone to convert this time to. + */ + public static long getNextMidnight(Time recycle, long theTime, String tz) { + if (recycle == null) { + recycle = new Time(); + } + recycle.timezone = tz; + recycle.set(theTime); + recycle.monthDay ++; + recycle.hour = 0; + recycle.minute = 0; + recycle.second = 0; + return recycle.normalize(true); + } + + public static void setAllowWeekForDetailView(boolean allowWeekView) { + mAllowWeekForDetailView = allowWeekView; + } + + public static boolean getAllowWeekForDetailView() { + return mAllowWeekForDetailView; + } + + public static boolean getConfigBool(Context c, int key) { + return c.getResources().getBoolean(key); + } + + /** + * For devices with Jellybean or later, darkens the given color to ensure that white text is + * clearly visible on top of it. For devices prior to Jellybean, does nothing, as the + * sync adapter handles the color change. + * + * @param color + */ + public static int getDisplayColorFromColor(int color) { + if (!isJellybeanOrLater()) { + return color; + } + + float[] hsv = new float[3]; + Color.colorToHSV(color, hsv); + hsv[1] = Math.min(hsv[1] * SATURATION_ADJUST, 1.0f); + hsv[2] = hsv[2] * INTENSITY_ADJUST; + return Color.HSVToColor(hsv); + } + + // This takes a color and computes what it would look like blended with + // white. The result is the color that should be used for declined events. + public static int getDeclinedColorFromColor(int color) { + int bg = 0xffffffff; + int a = DECLINED_EVENT_ALPHA; + int r = (((color & 0x00ff0000) * a) + ((bg & 0x00ff0000) * (0xff - a))) & 0xff000000; + int g = (((color & 0x0000ff00) * a) + ((bg & 0x0000ff00) * (0xff - a))) & 0x00ff0000; + int b = (((color & 0x000000ff) * a) + ((bg & 0x000000ff) * (0xff - a))) & 0x0000ff00; + return (0xff000000) | ((r | g | b) >> 8); + } + + public static void trySyncAndDisableUpgradeReceiver(Context context) { + final PackageManager pm = context.getPackageManager(); + ComponentName upgradeComponent = new ComponentName(context, UpgradeReceiver.class); + if (pm.getComponentEnabledSetting(upgradeComponent) == + PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { + // The upgrade receiver has been disabled, which means this code has been run before, + // so no need to sync. + return; + } + + Bundle extras = new Bundle(); + extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + ContentResolver.requestSync( + null /* no account */, + Calendars.CONTENT_URI.getAuthority(), + extras); + + // Now unregister the receiver so that we won't continue to sync every time. + pm.setComponentEnabledSetting(upgradeComponent, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + } + + // A single strand represents one color of events. Events are divided up by + // color to make them convenient to draw. The black strand is special in + // that it holds conflicting events as well as color settings for allday on + // each day. + public static class DNAStrand { + public float[] points; + public int[] allDays; // color for the allday, 0 means no event + int position; + public int color; + int count; + } + + // A segment is a single continuous length of time occupied by a single + // color. Segments should never span multiple days. + private static class DNASegment { + int startMinute; // in minutes since the start of the week + int endMinute; + int color; // Calendar color or black for conflicts + int day; // quick reference to the day this segment is on + } + + /** + * Converts a list of events to a list of segments to draw. Assumes list is + * ordered by start time of the events. The function processes events for a + * range of days from firstJulianDay to firstJulianDay + dayXs.length - 1. + * The algorithm goes over all the events and creates a set of segments + * ordered by start time. This list of segments is then converted into a + * HashMap of strands which contain the draw points and are organized by + * color. The strands can then be drawn by setting the paint color to each + * strand's color and calling drawLines on its set of points. The points are + * set up using the following parameters. + *
    + *
  • Events between midnight and WORK_DAY_START_MINUTES are compressed + * into the first 1/8th of the space between top and bottom.
  • + *
  • Events between WORK_DAY_END_MINUTES and the following midnight are + * compressed into the last 1/8th of the space between top and bottom
  • + *
  • Events between WORK_DAY_START_MINUTES and WORK_DAY_END_MINUTES use + * the remaining 3/4ths of the space
  • + *
  • All segments drawn will maintain at least minPixels height, except + * for conflicts in the first or last 1/8th, which may be smaller
  • + *
+ * + * @param firstJulianDay The julian day of the first day of events + * @param events A list of events sorted by start time + * @param top The lowest y value the dna should be drawn at + * @param bottom The highest y value the dna should be drawn at + * @param dayXs An array of x values to draw the dna at, one for each day + * @param conflictColor the color to use for conflicts + * @return + */ + public static HashMap createDNAStrands(int firstJulianDay, + ArrayList events, int top, int bottom, int minPixels, int[] dayXs, + Context context) { + + if (!mMinutesLoaded) { + if (context == null) { + Log.wtf(TAG, "No context and haven't loaded parameters yet! Can't create DNA."); + } + Resources res = context.getResources(); + CONFLICT_COLOR = res.getColor(R.color.month_dna_conflict_time_color); + WORK_DAY_START_MINUTES = res.getInteger(R.integer.work_start_minutes); + WORK_DAY_END_MINUTES = res.getInteger(R.integer.work_end_minutes); + WORK_DAY_END_LENGTH = DAY_IN_MINUTES - WORK_DAY_END_MINUTES; + WORK_DAY_MINUTES = WORK_DAY_END_MINUTES - WORK_DAY_START_MINUTES; + mMinutesLoaded = true; + } + + if (events == null || events.isEmpty() || dayXs == null || dayXs.length < 1 + || bottom - top < 8 || minPixels < 0) { + Log.e(TAG, + "Bad values for createDNAStrands! events:" + events + " dayXs:" + + Arrays.toString(dayXs) + " bot-top:" + (bottom - top) + " minPixels:" + + minPixels); + return null; + } + + LinkedList segments = new LinkedList(); + HashMap strands = new HashMap(); + // add a black strand by default, other colors will get added in + // the loop + DNAStrand blackStrand = new DNAStrand(); + blackStrand.color = CONFLICT_COLOR; + strands.put(CONFLICT_COLOR, blackStrand); + // the min length is the number of minutes that will occupy + // MIN_SEGMENT_PIXELS in the 'work day' time slot. This computes the + // minutes/pixel * minpx where the number of pixels are 3/4 the total + // dna height: 4*(mins/(px * 3/4)) + int minMinutes = minPixels * 4 * WORK_DAY_MINUTES / (3 * (bottom - top)); + + // There are slightly fewer than half as many pixels in 1/6 the space, + // so round to 2.5x for the min minutes in the non-work area + int minOtherMinutes = minMinutes * 5 / 2; + int lastJulianDay = firstJulianDay + dayXs.length - 1; + + Event event = new Event(); + // Go through all the events for the week + for (Event currEvent : events) { + // if this event is outside the weeks range skip it + if (currEvent.endDay < firstJulianDay || currEvent.startDay > lastJulianDay) { + continue; + } + if (currEvent.drawAsAllday()) { + addAllDayToStrands(currEvent, strands, firstJulianDay, dayXs.length); + continue; + } + // Copy the event over so we can clip its start and end to our range + currEvent.copyTo(event); + if (event.startDay < firstJulianDay) { + event.startDay = firstJulianDay; + event.startTime = 0; + } + // If it starts after the work day make sure the start is at least + // minPixels from midnight + if (event.startTime > DAY_IN_MINUTES - minOtherMinutes) { + event.startTime = DAY_IN_MINUTES - minOtherMinutes; + } + if (event.endDay > lastJulianDay) { + event.endDay = lastJulianDay; + event.endTime = DAY_IN_MINUTES - 1; + } + // If the end time is before the work day make sure it ends at least + // minPixels after midnight + if (event.endTime < minOtherMinutes) { + event.endTime = minOtherMinutes; + } + // If the start and end are on the same day make sure they are at + // least minPixels apart. This only needs to be done for times + // outside the work day as the min distance for within the work day + // is enforced in the segment code. + if (event.startDay == event.endDay && + event.endTime - event.startTime < minOtherMinutes) { + // If it's less than minPixels in an area before the work + // day + if (event.startTime < WORK_DAY_START_MINUTES) { + // extend the end to the first easy guarantee that it's + // minPixels + event.endTime = Math.min(event.startTime + minOtherMinutes, + WORK_DAY_START_MINUTES + minMinutes); + // if it's in the area after the work day + } else if (event.endTime > WORK_DAY_END_MINUTES) { + // First try shifting the end but not past midnight + event.endTime = Math.min(event.endTime + minOtherMinutes, DAY_IN_MINUTES - 1); + // if it's still too small move the start back + if (event.endTime - event.startTime < minOtherMinutes) { + event.startTime = event.endTime - minOtherMinutes; + } + } + } + + // This handles adding the first segment + if (segments.size() == 0) { + addNewSegment(segments, event, strands, firstJulianDay, 0, minMinutes); + continue; + } + // Now compare our current start time to the end time of the last + // segment in the list + DNASegment lastSegment = segments.getLast(); + int startMinute = (event.startDay - firstJulianDay) * DAY_IN_MINUTES + event.startTime; + int endMinute = Math.max((event.endDay - firstJulianDay) * DAY_IN_MINUTES + + event.endTime, startMinute + minMinutes); + + if (startMinute < 0) { + startMinute = 0; + } + if (endMinute >= WEEK_IN_MINUTES) { + endMinute = WEEK_IN_MINUTES - 1; + } + // If we start before the last segment in the list ends we need to + // start going through the list as this may conflict with other + // events + if (startMinute < lastSegment.endMinute) { + int i = segments.size(); + // find the last segment this event intersects with + while (--i >= 0 && endMinute < segments.get(i).startMinute); + + DNASegment currSegment; + // for each segment this event intersects with + for (; i >= 0 && startMinute <= (currSegment = segments.get(i)).endMinute; i--) { + // if the segment is already a conflict ignore it + if (currSegment.color == CONFLICT_COLOR) { + continue; + } + // if the event ends before the segment and wouldn't create + // a segment that is too small split off the right side + if (endMinute < currSegment.endMinute - minMinutes) { + DNASegment rhs = new DNASegment(); + rhs.endMinute = currSegment.endMinute; + rhs.color = currSegment.color; + rhs.startMinute = endMinute + 1; + rhs.day = currSegment.day; + currSegment.endMinute = endMinute; + segments.add(i + 1, rhs); + strands.get(rhs.color).count++; + if (DEBUG) { + Log.d(TAG, "Added rhs, curr:" + currSegment.toString() + " i:" + + segments.get(i).toString()); + } + } + // if the event starts after the segment and wouldn't create + // a segment that is too small split off the left side + if (startMinute > currSegment.startMinute + minMinutes) { + DNASegment lhs = new DNASegment(); + lhs.startMinute = currSegment.startMinute; + lhs.color = currSegment.color; + lhs.endMinute = startMinute - 1; + lhs.day = currSegment.day; + currSegment.startMinute = startMinute; + // increment i so that we are at the right position when + // referencing the segments to the right and left of the + // current segment. + segments.add(i++, lhs); + strands.get(lhs.color).count++; + if (DEBUG) { + Log.d(TAG, "Added lhs, curr:" + currSegment.toString() + " i:" + + segments.get(i).toString()); + } + } + // if the right side is black merge this with the segment to + // the right if they're on the same day and overlap + if (i + 1 < segments.size()) { + DNASegment rhs = segments.get(i + 1); + if (rhs.color == CONFLICT_COLOR && currSegment.day == rhs.day + && rhs.startMinute <= currSegment.endMinute + 1) { + rhs.startMinute = Math.min(currSegment.startMinute, rhs.startMinute); + segments.remove(currSegment); + strands.get(currSegment.color).count--; + // point at the new current segment + currSegment = rhs; + } + } + // if the left side is black merge this with the segment to + // the left if they're on the same day and overlap + if (i - 1 >= 0) { + DNASegment lhs = segments.get(i - 1); + if (lhs.color == CONFLICT_COLOR && currSegment.day == lhs.day + && lhs.endMinute >= currSegment.startMinute - 1) { + lhs.endMinute = Math.max(currSegment.endMinute, lhs.endMinute); + segments.remove(currSegment); + strands.get(currSegment.color).count--; + // point at the new current segment + currSegment = lhs; + // point i at the new current segment in case new + // code is added + i--; + } + } + // if we're still not black, decrement the count for the + // color being removed, change this to black, and increment + // the black count + if (currSegment.color != CONFLICT_COLOR) { + strands.get(currSegment.color).count--; + currSegment.color = CONFLICT_COLOR; + strands.get(CONFLICT_COLOR).count++; + } + } + + } + // If this event extends beyond the last segment add a new segment + if (endMinute > lastSegment.endMinute) { + addNewSegment(segments, event, strands, firstJulianDay, lastSegment.endMinute, + minMinutes); + } + } + weaveDNAStrands(segments, firstJulianDay, strands, top, bottom, dayXs); + return strands; + } + + // This figures out allDay colors as allDay events are found + private static void addAllDayToStrands(Event event, HashMap strands, + int firstJulianDay, int numDays) { + DNAStrand strand = getOrCreateStrand(strands, CONFLICT_COLOR); + // if we haven't initialized the allDay portion create it now + if (strand.allDays == null) { + strand.allDays = new int[numDays]; + } + + // For each day this event is on update the color + int end = Math.min(event.endDay - firstJulianDay, numDays - 1); + for (int i = Math.max(event.startDay - firstJulianDay, 0); i <= end; i++) { + if (strand.allDays[i] != 0) { + // if this day already had a color, it is now a conflict + strand.allDays[i] = CONFLICT_COLOR; + } else { + // else it's just the color of the event + strand.allDays[i] = event.color; + } + } + } + + // This processes all the segments, sorts them by color, and generates a + // list of points to draw + private static void weaveDNAStrands(LinkedList segments, int firstJulianDay, + HashMap strands, int top, int bottom, int[] dayXs) { + // First, get rid of any colors that ended up with no segments + Iterator strandIterator = strands.values().iterator(); + while (strandIterator.hasNext()) { + DNAStrand strand = strandIterator.next(); + if (strand.count < 1 && strand.allDays == null) { + strandIterator.remove(); + continue; + } + strand.points = new float[strand.count * 4]; + strand.position = 0; + } + // Go through each segment and compute its points + for (DNASegment segment : segments) { + // Add the points to the strand of that color + DNAStrand strand = strands.get(segment.color); + int dayIndex = segment.day - firstJulianDay; + int dayStartMinute = segment.startMinute % DAY_IN_MINUTES; + int dayEndMinute = segment.endMinute % DAY_IN_MINUTES; + int height = bottom - top; + int workDayHeight = height * 3 / 4; + int remainderHeight = (height - workDayHeight) / 2; + + int x = dayXs[dayIndex]; + int y0 = 0; + int y1 = 0; + + y0 = top + getPixelOffsetFromMinutes(dayStartMinute, workDayHeight, remainderHeight); + y1 = top + getPixelOffsetFromMinutes(dayEndMinute, workDayHeight, remainderHeight); + if (DEBUG) { + Log.d(TAG, "Adding " + Integer.toHexString(segment.color) + " at x,y0,y1: " + x + + " " + y0 + " " + y1 + " for " + dayStartMinute + " " + dayEndMinute); + } + strand.points[strand.position++] = x; + strand.points[strand.position++] = y0; + strand.points[strand.position++] = x; + strand.points[strand.position++] = y1; + } + } + + /** + * Compute a pixel offset from the top for a given minute from the work day + * height and the height of the top area. + */ + private static int getPixelOffsetFromMinutes(int minute, int workDayHeight, + int remainderHeight) { + int y; + if (minute < WORK_DAY_START_MINUTES) { + y = minute * remainderHeight / WORK_DAY_START_MINUTES; + } else if (minute < WORK_DAY_END_MINUTES) { + y = remainderHeight + (minute - WORK_DAY_START_MINUTES) * workDayHeight + / WORK_DAY_MINUTES; + } else { + y = remainderHeight + workDayHeight + (minute - WORK_DAY_END_MINUTES) * remainderHeight + / WORK_DAY_END_LENGTH; + } + return y; + } + + /** + * Add a new segment based on the event provided. This will handle splitting + * segments across day boundaries and ensures a minimum size for segments. + */ + private static void addNewSegment(LinkedList segments, Event event, + HashMap strands, int firstJulianDay, int minStart, int minMinutes) { + if (event.startDay > event.endDay) { + Log.wtf(TAG, "Event starts after it ends: " + event.toString()); + } + // If this is a multiday event split it up by day + if (event.startDay != event.endDay) { + Event lhs = new Event(); + lhs.color = event.color; + lhs.startDay = event.startDay; + // the first day we want the start time to be the actual start time + lhs.startTime = event.startTime; + lhs.endDay = lhs.startDay; + lhs.endTime = DAY_IN_MINUTES - 1; + // Nearly recursive iteration! + while (lhs.startDay != event.endDay) { + addNewSegment(segments, lhs, strands, firstJulianDay, minStart, minMinutes); + // The days in between are all day, even though that shouldn't + // actually happen due to the allday filtering + lhs.startDay++; + lhs.endDay = lhs.startDay; + lhs.startTime = 0; + minStart = 0; + } + // The last day we want the end time to be the actual end time + lhs.endTime = event.endTime; + event = lhs; + } + // Create the new segment and compute its fields + DNASegment segment = new DNASegment(); + int dayOffset = (event.startDay - firstJulianDay) * DAY_IN_MINUTES; + int endOfDay = dayOffset + DAY_IN_MINUTES - 1; + // clip the start if needed + segment.startMinute = Math.max(dayOffset + event.startTime, minStart); + // and extend the end if it's too small, but not beyond the end of the + // day + int minEnd = Math.min(segment.startMinute + minMinutes, endOfDay); + segment.endMinute = Math.max(dayOffset + event.endTime, minEnd); + if (segment.endMinute > endOfDay) { + segment.endMinute = endOfDay; + } + + segment.color = event.color; + segment.day = event.startDay; + segments.add(segment); + // increment the count for the correct color or add a new strand if we + // don't have that color yet + DNAStrand strand = getOrCreateStrand(strands, segment.color); + strand.count++; + } + + /** + * Try to get a strand of the given color. Create it if it doesn't exist. + */ + private static DNAStrand getOrCreateStrand(HashMap strands, int color) { + DNAStrand strand = strands.get(color); + if (strand == null) { + strand = new DNAStrand(); + strand.color = color; + strand.count = 0; + strands.put(strand.color, strand); + } + return strand; + } + + /** + * Sends an intent to launch the top level Calendar view. + * + * @param context + */ + public static void returnToCalendarHome(Context context) { + Intent launchIntent = new Intent(context, AllInOneActivity.class); + launchIntent.setAction(Intent.ACTION_DEFAULT); + launchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + launchIntent.putExtra(INTENT_KEY_HOME, true); + context.startActivity(launchIntent); + } + + /** + * Given a context and a time in millis since unix epoch figures out the + * correct week of the year for that time. + * + * @param millisSinceEpoch + * @return + */ + public static int getWeekNumberFromTime(long millisSinceEpoch, Context context) { + Time weekTime = new Time(getTimeZone(context, null)); + weekTime.set(millisSinceEpoch); + weekTime.normalize(true); + int firstDayOfWeek = getFirstDayOfWeek(context); + // if the date is on Saturday or Sunday and the start of the week + // isn't Monday we may need to shift the date to be in the correct + // week + if (weekTime.weekDay == Time.SUNDAY + && (firstDayOfWeek == Time.SUNDAY || firstDayOfWeek == Time.SATURDAY)) { + weekTime.monthDay++; + weekTime.normalize(true); + } else if (weekTime.weekDay == Time.SATURDAY && firstDayOfWeek == Time.SATURDAY) { + weekTime.monthDay += 2; + weekTime.normalize(true); + } + return weekTime.getWeekNumber(); + } + + /** + * Formats a day of the week string. This is either just the name of the day + * or a combination of yesterday/today/tomorrow and the day of the week. + * + * @param julianDay The julian day to get the string for + * @param todayJulianDay The julian day for today's date + * @param millis A utc millis since epoch time that falls on julian day + * @param context The calling context, used to get the timezone and do the + * formatting + * @return + */ + public static String getDayOfWeekString(int julianDay, int todayJulianDay, long millis, + Context context) { + getTimeZone(context, null); + int flags = DateUtils.FORMAT_SHOW_WEEKDAY; + String dayViewText; + if (julianDay == todayJulianDay) { + dayViewText = context.getString(R.string.agenda_today, + mTZUtils.formatDateRange(context, millis, millis, flags).toString()); + } else if (julianDay == todayJulianDay - 1) { + dayViewText = context.getString(R.string.agenda_yesterday, + mTZUtils.formatDateRange(context, millis, millis, flags).toString()); + } else if (julianDay == todayJulianDay + 1) { + dayViewText = context.getString(R.string.agenda_tomorrow, + mTZUtils.formatDateRange(context, millis, millis, flags).toString()); + } else { + dayViewText = mTZUtils.formatDateRange(context, millis, millis, flags).toString(); + } + dayViewText = dayViewText.toUpperCase(); + return dayViewText; + } + + // Calculate the time until midnight + 1 second and set the handler to + // do run the runnable + public static void setMidnightUpdater(Handler h, Runnable r, String timezone) { + if (h == null || r == null || timezone == null) { + return; + } + long now = System.currentTimeMillis(); + Time time = new Time(timezone); + time.set(now); + long runInMillis = (24 * 3600 - time.hour * 3600 - time.minute * 60 - + time.second + 1) * 1000; + h.removeCallbacks(r); + h.postDelayed(r, runInMillis); + } + + // Stop the midnight update thread + public static void resetMidnightUpdater(Handler h, Runnable r) { + if (h == null || r == null) { + return; + } + h.removeCallbacks(r); + } + + /** + * Returns a string description of the specified time interval. + */ + public static String getDisplayedDatetime(long startMillis, long endMillis, long currentMillis, + String localTimezone, boolean allDay, Context context) { + // Configure date/time formatting. + int flagsDate = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; + int flagsTime = DateUtils.FORMAT_SHOW_TIME; + if (DateFormat.is24HourFormat(context)) { + flagsTime |= DateUtils.FORMAT_24HOUR; + } + + Time currentTime = new Time(localTimezone); + currentTime.set(currentMillis); + Resources resources = context.getResources(); + String datetimeString = null; + if (allDay) { + // All day events require special timezone adjustment. + long localStartMillis = convertAlldayUtcToLocal(null, startMillis, localTimezone); + long localEndMillis = convertAlldayUtcToLocal(null, endMillis, localTimezone); + if (singleDayEvent(localStartMillis, localEndMillis, currentTime.gmtoff)) { + // If possible, use "Today" or "Tomorrow" instead of a full date string. + int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), + localStartMillis, currentMillis, currentTime.gmtoff); + if (TODAY == todayOrTomorrow) { + datetimeString = resources.getString(R.string.today); + } else if (TOMORROW == todayOrTomorrow) { + datetimeString = resources.getString(R.string.tomorrow); + } + } + if (datetimeString == null) { + // For multi-day allday events or single-day all-day events that are not + // today or tomorrow, use framework formatter. + Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault()); + datetimeString = DateUtils.formatDateRange(context, f, startMillis, + endMillis, flagsDate, Time.TIMEZONE_UTC).toString(); + } + } else { + if (singleDayEvent(startMillis, endMillis, currentTime.gmtoff)) { + // Format the time. + String timeString = Utils.formatDateRange(context, startMillis, endMillis, + flagsTime); + + // If possible, use "Today" or "Tomorrow" instead of a full date string. + int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), startMillis, + currentMillis, currentTime.gmtoff); + if (TODAY == todayOrTomorrow) { + // Example: "Today at 1:00pm - 2:00 pm" + datetimeString = resources.getString(R.string.today_at_time_fmt, + timeString); + } else if (TOMORROW == todayOrTomorrow) { + // Example: "Tomorrow at 1:00pm - 2:00 pm" + datetimeString = resources.getString(R.string.tomorrow_at_time_fmt, + timeString); + } else { + // Format the full date. Example: "Thursday, April 12, 1:00pm - 2:00pm" + String dateString = Utils.formatDateRange(context, startMillis, endMillis, + flagsDate); + datetimeString = resources.getString(R.string.date_time_fmt, dateString, + timeString); + } + } else { + // For multiday events, shorten day/month names. + // Example format: "Fri Apr 6, 5:00pm - Sun, Apr 8, 6:00pm" + int flagsDatetime = flagsDate | flagsTime | DateUtils.FORMAT_ABBREV_MONTH | + DateUtils.FORMAT_ABBREV_WEEKDAY; + datetimeString = Utils.formatDateRange(context, startMillis, endMillis, + flagsDatetime); + } + } + return datetimeString; + } + + /** + * Returns the timezone to display in the event info, if the local timezone is different + * from the event timezone. Otherwise returns null. + */ + public static String getDisplayedTimezone(long startMillis, String localTimezone, + String eventTimezone) { + String tzDisplay = null; + if (!TextUtils.equals(localTimezone, eventTimezone)) { + // Figure out if this is in DST + TimeZone tz = TimeZone.getTimeZone(localTimezone); + if (tz == null || tz.getID().equals("GMT")) { + tzDisplay = localTimezone; + } else { + Time startTime = new Time(localTimezone); + startTime.set(startMillis); + tzDisplay = tz.getDisplayName(startTime.isDst != 0, TimeZone.SHORT); + } + } + return tzDisplay; + } + + /** + * Returns whether the specified time interval is in a single day. + */ + private static boolean singleDayEvent(long startMillis, long endMillis, long localGmtOffset) { + if (startMillis == endMillis) { + return true; + } + + // An event ending at midnight should still be a single-day event, so check + // time end-1. + int startDay = Time.getJulianDay(startMillis, localGmtOffset); + int endDay = Time.getJulianDay(endMillis - 1, localGmtOffset); + return startDay == endDay; + } + + // Using int constants as a return value instead of an enum to minimize resources. + private static final int TODAY = 1; + private static final int TOMORROW = 2; + private static final int NONE = 0; + + /** + * Returns TODAY or TOMORROW if applicable. Otherwise returns NONE. + */ + private static int isTodayOrTomorrow(Resources r, long dayMillis, + long currentMillis, long localGmtOffset) { + int startDay = Time.getJulianDay(dayMillis, localGmtOffset); + int currentDay = Time.getJulianDay(currentMillis, localGmtOffset); + + int days = startDay - currentDay; + if (days == 1) { + return TOMORROW; + } else if (days == 0) { + return TODAY; + } else { + return NONE; + } + } + + /** + * Inserts a drawable with today's day into the today's icon in the option menu + * @param icon - today's icon from the options menu + */ + public static void setTodayIcon(LayerDrawable icon, Context c, String timezone) { + DayOfMonthDrawable today; + + // Reuse current drawable if possible + Drawable currentDrawable = icon.findDrawableByLayerId(R.id.today_icon_day); + if (currentDrawable != null && currentDrawable instanceof DayOfMonthDrawable) { + today = (DayOfMonthDrawable)currentDrawable; + } else { + today = new DayOfMonthDrawable(c); + } + // Set the day and update the icon + Time now = new Time(timezone); + now.setToNow(); + now.normalize(false); + today.setDayOfMonth(now.monthDay); + icon.mutate(); + icon.setDrawableByLayerId(R.id.today_icon_day, today); + } + + /** + * Get a list of quick responses used for emailing guests from the + * SharedPreferences. If not are found, get the hard coded ones that shipped + * with the app + * + * @param context + * @return a list of quick responses. + */ + public static String[] getQuickResponses(Context context) { + String[] s = Utils.getSharedPreference(context, KEY_QUICK_RESPONSES, (String[]) null); + + if (s == null) { + s = context.getResources().getStringArray(R.array.quick_response_defaults); + } + + return s; + } + + /** + * Return the app version code. + */ + public static String getVersionCode(Context context) { + if (sVersion == null) { + try { + sVersion = context.getPackageManager().getPackageInfo( + context.getPackageName(), 0).versionName; + } catch (PackageManager.NameNotFoundException e) { + // Can't find version; just leave it blank. + Log.e(TAG, "Error finding package " + context.getApplicationInfo().packageName); + } + } + return sVersion; + } +} -- cgit v1.2.3 From 8d904410ab2ccecdb7b97cca907e8307a2c47c1c Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 24 Jun 2021 17:09:02 +0000 Subject: AOSP/Calendar - Utils fully converted with bp file Using Android Studio, Utils.java has been fully converted to Kotlin. The converter did not automatically add override modifiers to those methods that required them. Moreover, @JvmField and @JvmStatic annotations had to be manually added to avoid an "unresolved symbol" error. Interestingly, a couple other files were causing runtime errors, resulting in the app to crash upon opening it. One of these errors had to do with a few casts from Int to Long that were attempted in CalendarViewAdapter.kt. Although this type of widening cast works without issues in Java, runtime errors will ensue in Kotlin. Instead, the .toLong() function must be called to correctly parse and convert the type rather than just cast it. Another runtime issue occurred due to an import in DayFragment.kt. The adb logcat suggested that a cast between android.view.ViewGroup.LayoutParams and android.widget.FrameLayout.LayoutParams was the culprit behind the errors. To solve this, I substituted the latter import for the former. SimpleWeekView had to be tweaked because it assigned a method that returns a nullable String? explicity to String. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.148 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 13m 25s Total aggregated tests run time: 13m 25s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.47 tests/sec [376 tests / 805156 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 7096 ms || clean = 1963 ms Total preparation time: 7s || Total tear down time: 1s ======================================================= =============== Summary =============== Total Run time: 14m 53s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: Ibe19564a7ac590461e6cba2c164624e99f48f319 --- Android.bp | 1 + src/com/android/calendar/CalendarViewAdapter.kt | 29 +- src/com/android/calendar/DayFragment.kt | 77 +- src/com/android/calendar/Utils.kt | 1712 +++++++++++----------- src/com/android/calendar/month/SimpleWeekView.kt | 2 +- 5 files changed, 951 insertions(+), 870 deletions(-) diff --git a/Android.bp b/Android.bp index 35866ce5..5cec2bc5 100644 --- a/Android.bp +++ b/Android.bp @@ -32,6 +32,7 @@ exclude_srcsd = [ "src/**/calendar/Event.java", "src/**/calendar/EventInfoActivity.java", "src/**/calendar/StickyHeaderListView.java", + "src/**/calendar/Utils.java", ] exclude_srcsm = [ diff --git a/src/com/android/calendar/CalendarViewAdapter.kt b/src/com/android/calendar/CalendarViewAdapter.kt index 920eec2e..2fe10272 100644 --- a/src/com/android/calendar/CalendarViewAdapter.kt +++ b/src/com/android/calendar/CalendarViewAdapter.kt @@ -54,8 +54,7 @@ class CalendarViewAdapter(context: Context, viewType: Int, showDate: Boolean) : private val mFormatter: Formatter private val mStringBuilder: StringBuilder private var mMidnightHandler: Handler? = null // Used to run a time update every midnight - private val mShowDate // Spinner mode indicator (view name or view name with date) - : Boolean + private val mShowDate: Boolean // Spinner mode indicator (view name or view name with date) // Updates time specific variables (time-zone, today's Julian day). private val mTimeUpdater: Runnable = object : Runnable { @@ -72,7 +71,7 @@ class CalendarViewAdapter(context: Context, viewType: Int, showDate: Boolean) : val time = Time(mTimeZone) val now: Long = System.currentTimeMillis() time.set(now) - mTodayJulianDay = Time.getJulianDay(now, time.gmtoff) as Long + mTodayJulianDay = Time.getJulianDay(now, time.gmtoff).toLong() notifyDataSetChanged() setMidnightHandler() } @@ -86,7 +85,7 @@ class CalendarViewAdapter(context: Context, viewType: Int, showDate: Boolean) : val time = Time(mTimeZone) time.set(now) val runInMillis: Long = ((24 * 3600 - time.hour * 3600 - time.minute * 60 - - time.second + 1) * 1000) as Long + time.second + 1) * 1000).toLong() mMidnightHandler?.postDelayed(mTimeUpdater, runInMillis) } @@ -195,27 +194,27 @@ class CalendarViewAdapter(context: Context, viewType: Int, showDate: Boolean) : } @Override - override fun getDropDownView(position: Int, convertView: View, parent: ViewGroup?): View { - var v: View = mInflater.inflate(R.layout.actionbar_pulldown_menu_button, parent, false) - val viewType: TextView = v.findViewById(R.id.button_view) as TextView - val date: TextView = v.findViewById(R.id.button_date) as TextView + override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup?): View? { + var v: View? = mInflater.inflate(R.layout.actionbar_pulldown_menu_button, parent, false) + val viewType: TextView? = v?.findViewById(R.id.button_view) as? TextView + val date: TextView? = v?.findViewById(R.id.button_date) as? TextView when (position) { DAY_BUTTON_INDEX -> { - viewType.setText(mButtonNames[DAY_BUTTON_INDEX]) + viewType?.setText(mButtonNames[DAY_BUTTON_INDEX]) if (mShowDate) { - date.setText(buildMonthDayDate()) + date?.setText(buildMonthDayDate()) } } WEEK_BUTTON_INDEX -> { - viewType.setText(mButtonNames[WEEK_BUTTON_INDEX]) + viewType?.setText(mButtonNames[WEEK_BUTTON_INDEX]) if (mShowDate) { - date.setText(buildWeekDate()) + date?.setText(buildWeekDate()) } } MONTH_BUTTON_INDEX -> { - viewType.setText(mButtonNames[MONTH_BUTTON_INDEX]) + viewType?.setText(mButtonNames[MONTH_BUTTON_INDEX]) if (mShowDate) { - date.setText(buildMonthDate()) + date?.setText(buildMonthDate()) } } else -> v = convertView @@ -242,7 +241,7 @@ class CalendarViewAdapter(context: Context, viewType: Int, showDate: Boolean) : private fun buildDayOfWeek(): String { val t = Time(mTimeZone) t.set(mMilliTime) - val julianDay: Long = Time.getJulianDay(mMilliTime, t.gmtoff) as Long + val julianDay: Long = Time.getJulianDay(mMilliTime, t.gmtoff).toLong() var dayOfWeek: String? = null mStringBuilder.setLength(0) dayOfWeek = if (julianDay == mTodayJulianDay) { diff --git a/src/com/android/calendar/DayFragment.kt b/src/com/android/calendar/DayFragment.kt index 49da8bdb..63800951 100644 --- a/src/com/android/calendar/DayFragment.kt +++ b/src/com/android/calendar/DayFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -24,7 +24,7 @@ import android.text.format.Time import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.ViewGroup.LayoutParams +import android.widget.FrameLayout.LayoutParams import android.view.animation.Animation import android.view.animation.AnimationUtils import android.widget.ProgressBar @@ -48,7 +48,7 @@ class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { if (!this@DayFragment.isAdded()) { return } - val tz: String = Utils.getTimeZone(getActivity(), this) + val tz: String? = Utils.getTimeZone(getActivity(), this) mSelectedDay.timezone = tz mSelectedDay.normalize(true) } @@ -78,13 +78,16 @@ class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { mEventLoader = EventLoader(context) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View { - val v: View = inflater.inflate(R.layout.day_activity, null) - mViewSwitcher = v.findViewById(R.id.switcher) as ViewSwitcher + override fun onCreateView( + inflater: LayoutInflater?, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val v: View? = inflater?.inflate(R.layout.day_activity, null) + mViewSwitcher = v?.findViewById(R.id.switcher) as? ViewSwitcher mViewSwitcher?.setFactory(this) mViewSwitcher?.getCurrentView()?.requestFocus() - (mViewSwitcher?.getCurrentView() as DayView).updateTitle() + (mViewSwitcher?.getCurrentView() as? DayView)?.updateTitle() return v } @@ -104,12 +107,12 @@ class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { mEventLoader!!.startBackgroundThread() mTZUpdater.run() eventsChanged() - var view: DayView = mViewSwitcher?.getCurrentView() as DayView - view.handleOnResume() - view.restartCurrentTimeUpdates() - view = mViewSwitcher?.getNextView() as DayView - view.handleOnResume() - view.restartCurrentTimeUpdates() + var view: DayView? = mViewSwitcher?.getCurrentView() as? DayView + view?.handleOnResume() + view?.restartCurrentTimeUpdates() + view = mViewSwitcher?.getNextView() as? DayView + view?.handleOnResume() + view?.restartCurrentTimeUpdates() } override fun onSaveInstanceState(outState: Bundle?) { @@ -118,15 +121,15 @@ class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { override fun onPause() { super.onPause() - var view: DayView = mViewSwitcher?.getCurrentView() as DayView - view.cleanup() - view = mViewSwitcher?.getNextView() as DayView - view.cleanup() + var view: DayView? = mViewSwitcher?.getCurrentView() as? DayView + view?.cleanup() + view = mViewSwitcher?.getNextView() as? DayView + view?.cleanup() mEventLoader!!.stopBackgroundThread() // Stop events cross-fade animation - view.stopEventsAnimation() - (mViewSwitcher?.getNextView() as DayView).stopEventsAnimation() + view?.stopEventsAnimation() + (mViewSwitcher?.getNextView() as? DayView)?.stopEventsAnimation() } fun startProgressSpinner() { @@ -145,13 +148,13 @@ class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { mSelectedDay.set(goToTime) return } - val currentView: DayView = mViewSwitcher?.getCurrentView() as DayView + val currentView: DayView? = mViewSwitcher?.getCurrentView() as? DayView // How does goTo time compared to what's already displaying? - val diff: Int = currentView.compareToVisibleTimeRange(goToTime) + val diff: Int = currentView?.compareToVisibleTimeRange(goToTime) as Int if (diff == 0) { // In visible range. No need to switch view - currentView.setSelected(goToTime, ignoreTime, animateToday) + currentView?.setSelected(goToTime, ignoreTime, animateToday) } else { // Figure out which way to animate if (diff > 0) { @@ -161,16 +164,16 @@ class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { mViewSwitcher?.setInAnimation(mInAnimationBackward) mViewSwitcher?.setOutAnimation(mOutAnimationBackward) } - val next: DayView = mViewSwitcher?.getNextView() as DayView + val next: DayView? = mViewSwitcher?.getNextView() as? DayView if (ignoreTime) { - next.setFirstVisibleHour(currentView.getFirstVisibleHour()) + next?.setFirstVisibleHour(currentView?.getFirstVisibleHour()) } - next.setSelected(goToTime, ignoreTime, animateToday) - next.reloadEvents() + next?.setSelected(goToTime, ignoreTime, animateToday) + next?.reloadEvents() mViewSwitcher?.showNext() - next.requestFocus() - next.updateTitle() - next.restartCurrentTimeUpdates() + next?.requestFocus() + next?.updateTitle() + next?.restartCurrentTimeUpdates() } } @@ -194,15 +197,15 @@ class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { if (mViewSwitcher == null) { return } - var view: DayView = mViewSwitcher?.getCurrentView() as DayView - view.clearCachedEvents() - view.reloadEvents() - view = mViewSwitcher?.getNextView() as DayView - view.clearCachedEvents() + var view: DayView? = mViewSwitcher?.getCurrentView() as? DayView + view?.clearCachedEvents() + view?.reloadEvents() + view = mViewSwitcher?.getNextView() as? DayView + view?.clearCachedEvents() } - val nextView: DayView - get() = mViewSwitcher?.getNextView() as DayView + val nextView: DayView? + get() = mViewSwitcher?.getNextView() as? DayView override val supportedEventTypes: Long get() = CalendarController.EventType.GO_TO or CalendarController.EventType.EVENTS_CHANGED diff --git a/src/com/android/calendar/Utils.kt b/src/com/android/calendar/Utils.kt index cc55c999..ef780485 100644 --- a/src/com/android/calendar/Utils.kt +++ b/src/com/android/calendar/Utils.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,242 +13,209 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar; - -import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; - -import android.accounts.Account; -import android.app.Activity; -import android.app.SearchManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.graphics.Color; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.provider.CalendarContract.Calendars; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.text.style.URLSpan; -import android.text.util.Linkify; -import android.util.Log; - -import com.android.calendar.CalendarController.ViewType; -import com.android.calendar.CalendarUtils.TimeZoneUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Formatter; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class Utils { - private static final boolean DEBUG = false; - private static final String TAG = "CalUtils"; +package com.android.calendar + +import android.app.Activity +import android.content.ComponentName +import android.content.ContentResolver +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.content.pm.PackageManager +import android.content.res.Resources +import android.database.Cursor +import android.database.MatrixCursor +import android.graphics.Color +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.provider.CalendarContract.Calendars +import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME +import android.text.TextUtils +import android.text.format.DateFormat +import android.text.format.DateUtils +import android.text.format.Time +import android.util.Log +import com.android.calendar.CalendarController.ViewType +import com.android.calendar.CalendarUtils.TimeZoneUtils +import java.util.ArrayList +import java.util.Arrays +import java.util.Calendar +import java.util.Formatter +import java.util.HashMap +import java.util.LinkedHashSet +import java.util.LinkedList +import java.util.List +import java.util.Locale +import java.util.TimeZone +import java.util.regex.Pattern + +object Utils { + private const val DEBUG = false + private const val TAG = "CalUtils" // Set to 0 until we have UI to perform undo - public static final long UNDO_DELAY = 0; + const val UNDO_DELAY: Long = 0 // For recurring events which instances of the series are being modified - public static final int MODIFY_UNINITIALIZED = 0; - public static final int MODIFY_SELECTED = 1; - public static final int MODIFY_ALL_FOLLOWING = 2; - public static final int MODIFY_ALL = 3; + const val MODIFY_UNINITIALIZED = 0 + const val MODIFY_SELECTED = 1 + const val MODIFY_ALL_FOLLOWING = 2 + const val MODIFY_ALL = 3 // When the edit event view finishes it passes back the appropriate exit // code. - public static final int DONE_REVERT = 1 << 0; - public static final int DONE_SAVE = 1 << 1; - public static final int DONE_DELETE = 1 << 2; + const val DONE_REVERT = 1 shl 0 + const val DONE_SAVE = 1 shl 1 + const val DONE_DELETE = 1 shl 2 + // And should re run with DONE_EXIT if it should also leave the view, just // exiting is identical to reverting - public static final int DONE_EXIT = 1 << 0; - - public static final String OPEN_EMAIL_MARKER = " <"; - public static final String CLOSE_EMAIL_MARKER = ">"; - - public static final String INTENT_KEY_DETAIL_VIEW = "DETAIL_VIEW"; - public static final String INTENT_KEY_VIEW_TYPE = "VIEW"; - public static final String INTENT_VALUE_VIEW_TYPE_DAY = "DAY"; - public static final String INTENT_KEY_HOME = "KEY_HOME"; - - public static final int MONDAY_BEFORE_JULIAN_EPOCH = Time.EPOCH_JULIAN_DAY - 3; - public static final int DECLINED_EVENT_ALPHA = 0x66; - public static final int DECLINED_EVENT_TEXT_ALPHA = 0xC0; - - private static final float SATURATION_ADJUST = 1.3f; - private static final float INTENSITY_ADJUST = 0.8f; + const val DONE_EXIT = 1 shl 0 + const val OPEN_EMAIL_MARKER = " <" + const val CLOSE_EMAIL_MARKER = ">" + const val INTENT_KEY_DETAIL_VIEW = "DETAIL_VIEW" + const val INTENT_KEY_VIEW_TYPE = "VIEW" + const val INTENT_VALUE_VIEW_TYPE_DAY = "DAY" + const val INTENT_KEY_HOME = "KEY_HOME" + val MONDAY_BEFORE_JULIAN_EPOCH: Int = Time.EPOCH_JULIAN_DAY - 3 + const val DECLINED_EVENT_ALPHA = 0x66 + const val DECLINED_EVENT_TEXT_ALPHA = 0xC0 + private const val SATURATION_ADJUST = 1.3f + private const val INTENSITY_ADJUST = 0.8f // Defines used by the DNA generation code - static final int DAY_IN_MINUTES = 60 * 24; - static final int WEEK_IN_MINUTES = DAY_IN_MINUTES * 7; - // The work day is being counted as 6am to 8pm - static int WORK_DAY_MINUTES = 14 * 60; - static int WORK_DAY_START_MINUTES = 6 * 60; - static int WORK_DAY_END_MINUTES = 20 * 60; - static int WORK_DAY_END_LENGTH = (24 * 60) - WORK_DAY_END_MINUTES; - static int CONFLICT_COLOR = 0xFF000000; - static boolean mMinutesLoaded = false; + const val DAY_IN_MINUTES = 60 * 24 + const val WEEK_IN_MINUTES = DAY_IN_MINUTES * 7 - public static final int YEAR_MIN = 1970; - public static final int YEAR_MAX = 2036; + // The work day is being counted as 6am to 8pm + var WORK_DAY_MINUTES = 14 * 60 + var WORK_DAY_START_MINUTES = 6 * 60 + var WORK_DAY_END_MINUTES = 20 * 60 + var WORK_DAY_END_LENGTH = 24 * 60 - WORK_DAY_END_MINUTES + var CONFLICT_COLOR = -0x1000000 + var mMinutesLoaded = false + const val YEAR_MIN = 1970 + const val YEAR_MAX = 2036 // The name of the shared preferences file. This name must be maintained for // historical // reasons, as it's what PreferenceManager assigned the first time the file // was created. - static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; - - public static final String KEY_QUICK_RESPONSES = "preferences_quick_responses"; - - public static final String KEY_ALERTS_VIBRATE_WHEN = "preferences_alerts_vibrateWhen"; - - public static final String APPWIDGET_DATA_TYPE = "vnd.android.data/update"; - - static final String MACHINE_GENERATED_ADDRESS = "calendar.google.com"; - - private static final TimeZoneUtils mTZUtils = new TimeZoneUtils(SHARED_PREFS_NAME); - private static boolean mAllowWeekForDetailView = false; - private static long mTardis = 0; - private static String sVersion = null; - - private static final Pattern mWildcardPattern = Pattern.compile("^.*$"); + const val SHARED_PREFS_NAME = "com.android.calendar_preferences" + const val KEY_QUICK_RESPONSES = "preferences_quick_responses" + const val KEY_ALERTS_VIBRATE_WHEN = "preferences_alerts_vibrateWhen" + const val APPWIDGET_DATA_TYPE = "vnd.android.data/update" + const val MACHINE_GENERATED_ADDRESS = "calendar.google.com" + private val mTZUtils: TimeZoneUtils? = TimeZoneUtils(SHARED_PREFS_NAME) + @JvmField var allowWeekForDetailView = false + internal var tardis: Long = 0 + private set + private var sVersion: String? = null + private val mWildcardPattern: Pattern = Pattern.compile("^.*$") /** - * A coordinate must be of the following form for Google Maps to correctly use it: - * Latitude, Longitude - * - * This may be in decimal form: - * Latitude: {-90 to 90} - * Longitude: {-180 to 180} - * - * Or, in degrees, minutes, and seconds: - * Latitude: {-90 to 90}° {0 to 59}' {0 to 59}" - * Latitude: {-180 to 180}° {0 to 59}' {0 to 59}" - * + or - degrees may also be represented with N or n, S or s for latitude, and with - * E or e, W or w for longitude, where the direction may either precede or follow the value. - * - * Some examples of coordinates that will be accepted by the regex: - * 37.422081°, -122.084576° - * 37.422081,-122.084576 - * +37°25'19.49", -122°5'4.47" - * 37°25'19.49"N, 122°5'4.47"W - * N 37° 25' 19.49", W 122° 5' 4.47" - **/ - private static final String COORD_DEGREES_LATITUDE = - "([-+NnSs]" + "(\\s)*)?" - + "[1-9]?[0-9](\u00B0)" + "(\\s)*" - + "([1-5]?[0-9]\')?" + "(\\s)*" - + "([1-5]?[0-9]" + "(\\.[0-9]+)?\")?" - + "((\\s)*" + "[NnSs])?"; - private static final String COORD_DEGREES_LONGITUDE = - "([-+EeWw]" + "(\\s)*)?" - + "(1)?[0-9]?[0-9](\u00B0)" + "(\\s)*" - + "([1-5]?[0-9]\')?" + "(\\s)*" - + "([1-5]?[0-9]" + "(\\.[0-9]+)?\")?" - + "((\\s)*" + "[EeWw])?"; - private static final String COORD_DEGREES_PATTERN = - COORD_DEGREES_LATITUDE - + "(\\s)*" + "," + "(\\s)*" - + COORD_DEGREES_LONGITUDE; - private static final String COORD_DECIMAL_LATITUDE = - "[+-]?" - + "[1-9]?[0-9]" + "(\\.[0-9]+)" - + "(\u00B0)?"; - private static final String COORD_DECIMAL_LONGITUDE = - "[+-]?" - + "(1)?[0-9]?[0-9]" + "(\\.[0-9]+)" - + "(\u00B0)?"; - private static final String COORD_DECIMAL_PATTERN = - COORD_DECIMAL_LATITUDE - + "(\\s)*" + "," + "(\\s)*" - + COORD_DECIMAL_LONGITUDE; - private static final Pattern COORD_PATTERN = - Pattern.compile(COORD_DEGREES_PATTERN + "|" + COORD_DECIMAL_PATTERN); - - private static final String NANP_ALLOWED_SYMBOLS = "()+-*#."; - private static final int NANP_MIN_DIGITS = 7; - private static final int NANP_MAX_DIGITS = 11; - + * A coordinate must be of the following form for Google Maps to correctly use it: + * Latitude, Longitude + * + * This may be in decimal form: + * Latitude: {-90 to 90} + * Longitude: {-180 to 180} + * + * Or, in degrees, minutes, and seconds: + * Latitude: {-90 to 90}° {0 to 59}' {0 to 59}" + * Latitude: {-180 to 180}° {0 to 59}' {0 to 59}" + * + or - degrees may also be represented with N or n, S or s for latitude, and with + * E or e, W or w for longitude, where the direction may either precede or follow the value. + * + * Some examples of coordinates that will be accepted by the regex: + * 37.422081°, -122.084576° + * 37.422081,-122.084576 + * +37°25'19.49", -122°5'4.47" + * 37°25'19.49"N, 122°5'4.47"W + * N 37° 25' 19.49", W 122° 5' 4.47" + */ + private const val COORD_DEGREES_LATITUDE = ("([-+NnSs]" + "(\\s)*)?" + + "[1-9]?[0-9](\u00B0)" + "(\\s)*" + + "([1-5]?[0-9]\')?" + "(\\s)*" + + "([1-5]?[0-9]" + "(\\.[0-9]+)?\")?" + + "((\\s)*" + "[NnSs])?") + private const val COORD_DEGREES_LONGITUDE = ("([-+EeWw]" + "(\\s)*)?" + + "(1)?[0-9]?[0-9](\u00B0)" + "(\\s)*" + + "([1-5]?[0-9]\')?" + "(\\s)*" + + "([1-5]?[0-9]" + "(\\.[0-9]+)?\")?" + + "((\\s)*" + "[EeWw])?") + private const val COORD_DEGREES_PATTERN = (COORD_DEGREES_LATITUDE + "(\\s)*" + "," + "(\\s)*" + + COORD_DEGREES_LONGITUDE) + private const val COORD_DECIMAL_LATITUDE = ("[+-]?" + + "[1-9]?[0-9]" + "(\\.[0-9]+)" + + "(\u00B0)?") + private const val COORD_DECIMAL_LONGITUDE = ("[+-]?" + + "(1)?[0-9]?[0-9]" + "(\\.[0-9]+)" + + "(\u00B0)?") + private const val COORD_DECIMAL_PATTERN = (COORD_DECIMAL_LATITUDE + "(\\s)*" + "," + "(\\s)*" + + COORD_DECIMAL_LONGITUDE) + private val COORD_PATTERN: Pattern = + Pattern.compile(COORD_DEGREES_PATTERN + "|" + COORD_DECIMAL_PATTERN) + private const val NANP_ALLOWED_SYMBOLS = "()+-*#." + private const val NANP_MIN_DIGITS = 7 + private const val NANP_MAX_DIGITS = 11 /** - * Returns whether the SDK is the Jellybean release or later. + * Returns whether the SDK is the KeyLimePie release or later. */ - public static boolean isJellybeanOrLater() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; + @JvmStatic fun isKeyLimePieOrLater(): Boolean { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT } /** - * Returns whether the SDK is the KeyLimePie release or later. + * Returns whether the SDK is the Jellybean release or later. */ - public static boolean isKeyLimePieOrLater() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + @JvmStatic fun isJellybeanOrLater(): Boolean { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN } - public static int getViewTypeFromIntentAndSharedPref(Activity activity) { - Intent intent = activity.getIntent(); - Bundle extras = intent.getExtras(); - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(activity); - - if (TextUtils.equals(intent.getAction(), Intent.ACTION_EDIT)) { - return ViewType.EDIT; + @JvmStatic fun getViewTypeFromIntentAndSharedPref(activity: Activity): Int { + val intent: Intent? = activity.getIntent() + val extras: Bundle? = intent?.getExtras() + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(activity) + if (TextUtils.equals(intent?.getAction(), Intent.ACTION_EDIT)) { + return ViewType.EDIT } if (extras != null) { - if (extras.getBoolean(INTENT_KEY_DETAIL_VIEW, false)) { + if (extras?.getBoolean(INTENT_KEY_DETAIL_VIEW, false)) { // This is the "detail" view which is either agenda or day view - return prefs.getInt(GeneralPreferences.KEY_DETAILED_VIEW, - GeneralPreferences.DEFAULT_DETAILED_VIEW); - } else if (INTENT_VALUE_VIEW_TYPE_DAY.equals(extras.getString(INTENT_KEY_VIEW_TYPE))) { + return prefs?.getInt( + GeneralPreferences.KEY_DETAILED_VIEW, + GeneralPreferences.DEFAULT_DETAILED_VIEW + ) as Int + } else if (INTENT_VALUE_VIEW_TYPE_DAY.equals(extras?.getString(INTENT_KEY_VIEW_TYPE))) { // Not sure who uses this. This logic came from LaunchActivity - return ViewType.DAY; + return ViewType.DAY } } // Default to the last view - return prefs.getInt( - GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW); + return prefs?.getInt( + GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW + ) as Int } /** * Gets the intent action for telling the widget to update. */ - public static String getWidgetUpdateAction(Context context) { - return context.getPackageName() + ".APPWIDGET_UPDATE"; + @JvmStatic fun getWidgetUpdateAction(context: Context): String { + return context.getPackageName().toString() + ".APPWIDGET_UPDATE" } /** * Gets the intent action for telling the widget to update. */ - public static String getWidgetScheduledUpdateAction(Context context) { - return context.getPackageName() + ".APPWIDGET_SCHEDULED_UPDATE"; + @JvmStatic fun getWidgetScheduledUpdateAction(context: Context): String { + return context.getPackageName().toString() + ".APPWIDGET_SCHEDULED_UPDATE" } /** @@ -260,8 +227,8 @@ public class Utils { * @param context The calling activity * @param timeZone The time zone to set Calendar to, or **tbd** */ - public static void setTimeZone(Context context, String timeZone) { - mTZUtils.setTimeZone(context, timeZone); + @JvmStatic fun setTimeZone(context: Context?, timeZone: String?) { + mTZUtils?.setTimeZone(context as Context, timeZone as String) } /** @@ -275,12 +242,12 @@ public class Utils { * * @param context The calling activity * @param callback The runnable that should execute if a query returns new - * values + * values * @return The string value representing the time zone Calendar should - * display + * display */ - public static String getTimeZone(Context context, Runnable callback) { - return mTZUtils.getTimeZone(context, callback); + @JvmStatic fun getTimeZone(context: Context?, callback: Runnable?): String? { + return mTZUtils?.getTimeZone(context as Context, callback) } /** @@ -289,58 +256,79 @@ public class Utils { * @param context the context is required only if the time is shown * @param startMillis the start time in UTC milliseconds * @param endMillis the end time in UTC milliseconds - * @param flags a bit mask of options See {@link DateUtils#formatDateRange(Context, Formatter, - * long, long, int, String) formatDateRange} + * @param flags a bit mask of options See [formatDateRange][DateUtils.formatDateRange] * @return a string containing the formatted date/time range. */ - public static String formatDateRange( - Context context, long startMillis, long endMillis, int flags) { - return mTZUtils.formatDateRange(context, startMillis, endMillis, flags); - } - - public static boolean getDefaultVibrate(Context context, SharedPreferences prefs) { - boolean vibrate; - if (prefs.contains(KEY_ALERTS_VIBRATE_WHEN)) { + @JvmStatic fun formatDateRange( + context: Context?, + startMillis: Long, + endMillis: Long, + flags: Int + ): String? { + return mTZUtils?.formatDateRange(context as Context, startMillis, endMillis, flags) + } + + @JvmStatic fun getDefaultVibrate(context: Context, prefs: SharedPreferences?): Boolean { + val vibrate: Boolean + if (prefs?.contains(KEY_ALERTS_VIBRATE_WHEN) == true) { // Migrate setting to new 4.2 behavior // // silent and never -> off // always -> on - String vibrateWhen = prefs.getString(KEY_ALERTS_VIBRATE_WHEN, null); - vibrate = vibrateWhen != null && vibrateWhen.equals(context - .getString(R.string.prefDefault_alerts_vibrate_true)); - prefs.edit().remove(KEY_ALERTS_VIBRATE_WHEN).commit(); - Log.d(TAG, "Migrating KEY_ALERTS_VIBRATE_WHEN(" + vibrateWhen - + ") to KEY_ALERTS_VIBRATE = " + vibrate); + val vibrateWhen: String? = prefs?.getString(KEY_ALERTS_VIBRATE_WHEN, null) + vibrate = vibrateWhen != null && vibrateWhen.equals( + context + .getString(R.string.prefDefault_alerts_vibrate_true) + ) + prefs?.edit().remove(KEY_ALERTS_VIBRATE_WHEN).commit() + Log.d( + TAG, "Migrating KEY_ALERTS_VIBRATE_WHEN(" + + vibrateWhen + ") to KEY_ALERTS_VIBRATE = " + vibrate + ) } else { - vibrate = prefs.getBoolean(GeneralPreferences.KEY_ALERTS_VIBRATE, - false); + vibrate = prefs?.getBoolean( + GeneralPreferences.KEY_ALERTS_VIBRATE, + false + ) as Boolean } - return vibrate; + return vibrate } - public static String[] getSharedPreference(Context context, String key, String[] defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - Set ss = prefs.getStringSet(key, null); + @JvmStatic fun getSharedPreference( + context: Context?, + key: String?, + defaultValue: Array? + ): Array? { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + val ss = prefs?.getStringSet(key, null) if (ss != null) { - String strings[] = new String[ss.size()]; - return ss.toArray(strings); + val strings = arrayOfNulls(ss?.size) + return ss?.toTypedArray() } - return defaultValue; + return defaultValue } - public static String getSharedPreference(Context context, String key, String defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getString(key, defaultValue); + @JvmStatic fun getSharedPreference( + context: Context?, + key: String?, + defaultValue: String? + ): String? { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + return prefs?.getString(key, defaultValue) } - public static int getSharedPreference(Context context, String key, int defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getInt(key, defaultValue); + @JvmStatic fun getSharedPreference(context: Context?, key: String?, defaultValue: Int): Int { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + return prefs?.getInt(key, defaultValue) as Int } - public static boolean getSharedPreference(Context context, String key, boolean defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getBoolean(key, defaultValue); + @JvmStatic fun getSharedPreference( + context: Context?, + key: String?, + defaultValue: Boolean + ): Boolean { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + return prefs?.getBoolean(key, defaultValue) as Boolean } /** @@ -350,155 +338,148 @@ public class Utils { * @param key the key of the preference to set * @param value the value to set */ - public static void setSharedPreference(Context context, String key, String value) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - prefs.edit().putString(key, value).apply(); + @JvmStatic fun setSharedPreference(context: Context?, key: String?, value: String?) { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + prefs?.edit()?.putString(key, value)?.apply() } - public static void setSharedPreference(Context context, String key, String[] values) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - LinkedHashSet set = new LinkedHashSet(); - for (String value : values) { - set.add(value); + @JvmStatic fun setSharedPreference(context: Context?, key: String?, values: Array) { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + val set: LinkedHashSet = LinkedHashSet() + for (value in values) { + set.add(value) } - prefs.edit().putStringSet(key, set).apply(); - } - - protected static void tardis() { - mTardis = System.currentTimeMillis(); + prefs?.edit()?.putStringSet(key, set)?.apply() } - protected static long getTardis() { - return mTardis; + internal fun tardis() { + tardis = System.currentTimeMillis() } - public static void setSharedPreference(Context context, String key, boolean value) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(key, value); - editor.apply(); + @JvmStatic fun setSharedPreference(context: Context?, key: String?, value: Boolean) { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + val editor: SharedPreferences.Editor? = prefs?.edit() + editor?.putBoolean(key, value) + editor?.apply() } - static void setSharedPreference(Context context, String key, int value) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - editor.putInt(key, value); - editor.apply(); + @JvmStatic fun setSharedPreference(context: Context?, key: String?, value: Int) { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + val editor: SharedPreferences.Editor? = prefs?.edit() + editor?.putInt(key, value) + editor?.apply() } - public static void removeSharedPreference(Context context, String key) { - SharedPreferences prefs = context.getSharedPreferences( - GeneralPreferences.SHARED_PREFS_NAME, Context.MODE_PRIVATE); - prefs.edit().remove(key).apply(); + @JvmStatic fun removeSharedPreference(context: Context?, key: String?) { + val prefs: SharedPreferences? = context?.getSharedPreferences( + GeneralPreferences.SHARED_PREFS_NAME, Context.MODE_PRIVATE + ) + prefs?.edit()?.remove(key)?.apply() } /** * Save default agenda/day/week/month view for next time * * @param context - * @param viewId {@link CalendarController.ViewType} + * @param viewId [CalendarController.ViewType] */ - static void setDefaultView(Context context, int viewId) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - - boolean validDetailView = false; - if (mAllowWeekForDetailView && viewId == CalendarController.ViewType.WEEK) { - validDetailView = true; - } else { - validDetailView = viewId == CalendarController.ViewType.AGENDA - || viewId == CalendarController.ViewType.DAY; - } - + @JvmStatic fun setDefaultView(context: Context?, viewId: Int) { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + val editor: SharedPreferences.Editor? = prefs?.edit() + var validDetailView = false + validDetailView = + if (allowWeekForDetailView && viewId == CalendarController.ViewType.WEEK) { + true + } else { + (viewId == CalendarController.ViewType.AGENDA || + viewId == CalendarController.ViewType.DAY) + } if (validDetailView) { // Record the detail start view - editor.putInt(GeneralPreferences.KEY_DETAILED_VIEW, viewId); + editor?.putInt(GeneralPreferences.KEY_DETAILED_VIEW, viewId) } // Record the (new) start view - editor.putInt(GeneralPreferences.KEY_START_VIEW, viewId); - editor.apply(); + editor?.putInt(GeneralPreferences.KEY_START_VIEW, viewId) + editor?.apply() } - public static MatrixCursor matrixCursorFromCursor(Cursor cursor) { + @JvmStatic fun matrixCursorFromCursor(cursor: Cursor?): MatrixCursor? { if (cursor == null) { - return null; + return null } - - String[] columnNames = cursor.getColumnNames(); + var columnNames: Array = cursor.getColumnNames() if (columnNames == null) { - columnNames = new String[] {}; + columnNames = arrayOf() } - MatrixCursor newCursor = new MatrixCursor(columnNames); - int numColumns = cursor.getColumnCount(); - String data[] = new String[numColumns]; - cursor.moveToPosition(-1); + val newCursor = MatrixCursor(columnNames) + val numColumns: Int = cursor.getColumnCount() + val data = arrayOfNulls(numColumns) + cursor.moveToPosition(-1) while (cursor.moveToNext()) { - for (int i = 0; i < numColumns; i++) { - data[i] = cursor.getString(i); + for (i in 0 until numColumns) { + data[i] = cursor.getString(i) } - newCursor.addRow(data); + newCursor.addRow(data) } - return newCursor; + return newCursor } /** * Compares two cursors to see if they contain the same data. * * @return Returns true of the cursors contain the same data and are not - * null, false otherwise + * null, false otherwise */ - public static boolean compareCursors(Cursor c1, Cursor c2) { + @JvmStatic fun compareCursors(c1: Cursor?, c2: Cursor?): Boolean { if (c1 == null || c2 == null) { - return false; + return false } - - int numColumns = c1.getColumnCount(); + val numColumns: Int = c1.getColumnCount() if (numColumns != c2.getColumnCount()) { - return false; + return false } - - if (c1.getCount() != c2.getCount()) { - return false; + if (c1.getCount() !== c2.getCount()) { + return false } - - c1.moveToPosition(-1); - c2.moveToPosition(-1); + c1.moveToPosition(-1) + c2.moveToPosition(-1) while (c1.moveToNext() && c2.moveToNext()) { - for (int i = 0; i < numColumns; i++) { + for (i in 0 until numColumns) { if (!TextUtils.equals(c1.getString(i), c2.getString(i))) { - return false; + return false } } } - - return true; + return true } /** * If the given intent specifies a time (in milliseconds since the epoch), * then that time is returned. Otherwise, the current time is returned. */ - public static final long timeFromIntentInMillis(Intent intent) { + @JvmStatic fun timeFromIntentInMillis(intent: Intent?): Long? { // If the time was specified, then use that. Otherwise, use the current // time. - Uri data = intent.getData(); - long millis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1); - if (millis == -1 && data != null && data.isHierarchical()) { - List path = data.getPathSegments(); - if (path.size() == 2 && path.get(0).equals("time")) { + val data: Uri? = intent?.getData() + var millis: Long? = intent?.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1)?.toLong() + if (millis == -1L && data != null && data?.isHierarchical()) { + val path: List = data?.getPathSegments() as List + if (path.size == 2 && path[0].equals("time")) { try { - millis = Long.valueOf(data.getLastPathSegment()); - } catch (NumberFormatException e) { - Log.i("Calendar", "timeFromIntentInMillis: Data existed but no valid time " - + "found. Using current time."); + millis = (data?.getLastPathSegment()?.toLong()) + } catch (e: NumberFormatException) { + Log.i( + "Calendar", "timeFromIntentInMillis: Data existed but no valid time " + + "found. Using current time." + ) } } } - if (millis <= 0) { - millis = System.currentTimeMillis(); + if ((millis ?: 0L) <= 0) { + millis = System.currentTimeMillis() } - return millis; + return millis } /** @@ -508,11 +489,11 @@ public class Utils { * @param time the time to format * @return the string containing the weekday and the date */ - public static String formatMonthYear(Context context, Time time) { - int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY - | DateUtils.FORMAT_SHOW_YEAR; - long millis = time.toMillis(true); - return formatDateRange(context, millis, millis, flags); + @JvmStatic fun formatMonthYear(context: Context?, time: Time): String? { + val flags: Int = (DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_MONTH_DAY + or DateUtils.FORMAT_SHOW_YEAR) + val millis: Long = time.toMillis(true) + return formatDateRange(context, millis, millis, flags) } /** @@ -523,55 +504,55 @@ public class Utils { * @param delim the delimiter to use * @return a string contained the things joined together */ - public static String join(List things, String delim) { - StringBuilder builder = new StringBuilder(); - boolean first = true; - for (Object thing : things) { + @JvmStatic fun join(things: List<*>, delim: String?): String { + val builder = StringBuilder() + var first = true + for (thing in things) { if (first) { - first = false; + first = false } else { - builder.append(delim); + builder.append(delim) } - builder.append(thing.toString()); + builder.append(thing.toString()) } - return builder.toString(); + return builder.toString() } /** - * Returns the week since {@link Time#EPOCH_JULIAN_DAY} (Jan 1, 1970) + * Returns the week since [Time.EPOCH_JULIAN_DAY] (Jan 1, 1970) * adjusted for first day of week. * * This takes a julian day and the week start day and calculates which - * week since {@link Time#EPOCH_JULIAN_DAY} that day occurs in, starting + * week since [Time.EPOCH_JULIAN_DAY] that day occurs in, starting * at 0. *Do not* use this to compute the ISO week number for the year. * * @param julianDay The julian day to calculate the week number for * @param firstDayOfWeek Which week day is the first day of the week, - * see {@link Time#SUNDAY} + * see [Time.SUNDAY] * @return Weeks since the epoch */ - public static int getWeeksSinceEpochFromJulianDay(int julianDay, int firstDayOfWeek) { - int diff = Time.THURSDAY - firstDayOfWeek; + @JvmStatic fun getWeeksSinceEpochFromJulianDay(julianDay: Int, firstDayOfWeek: Int): Int { + var diff: Int = Time.THURSDAY - firstDayOfWeek if (diff < 0) { - diff += 7; + diff += 7 } - int refDay = Time.EPOCH_JULIAN_DAY - diff; - return (julianDay - refDay) / 7; + val refDay: Int = Time.EPOCH_JULIAN_DAY - diff + return (julianDay - refDay) / 7 } /** * Takes a number of weeks since the epoch and calculates the Julian day of * the Monday for that week. * - * This assumes that the week containing the {@link Time#EPOCH_JULIAN_DAY} + * This assumes that the week containing the [Time.EPOCH_JULIAN_DAY] * is considered week 0. It returns the Julian day for the Monday - * {@code week} weeks after the Monday of the week containing the epoch. + * `week` weeks after the Monday of the week containing the epoch. * * @param week Number of weeks since the epoch * @return The julian day for the Monday of the given week since the epoch */ - public static int getJulianMondayFromWeeksSinceEpoch(int week) { - return MONDAY_BEFORE_JULIAN_EPOCH + week * 7; + @JvmStatic fun getJulianMondayFromWeeksSinceEpoch(week: Int): Int { + return MONDAY_BEFORE_JULIAN_EPOCH + week * 7 } /** @@ -579,24 +560,23 @@ public class Utils { * * @return the first day of week in android.text.format.Time */ - public static int getFirstDayOfWeek(Context context) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - String pref = prefs.getString( - GeneralPreferences.KEY_WEEK_START_DAY, GeneralPreferences.WEEK_START_DEFAULT); - - int startDay; - if (GeneralPreferences.WEEK_START_DEFAULT.equals(pref)) { - startDay = Calendar.getInstance().getFirstDayOfWeek(); + @JvmStatic fun getFirstDayOfWeek(context: Context?): Int { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + val pref: String? = prefs?.getString( + GeneralPreferences.KEY_WEEK_START_DAY, GeneralPreferences.WEEK_START_DEFAULT + ) + val startDay: Int + startDay = if (GeneralPreferences.WEEK_START_DEFAULT.equals(pref)) { + Calendar.getInstance().getFirstDayOfWeek() } else { - startDay = Integer.parseInt(pref); + Integer.parseInt(pref) } - - if (startDay == Calendar.SATURDAY) { - return Time.SATURDAY; + return if (startDay == Calendar.SATURDAY) { + Time.SATURDAY } else if (startDay == Calendar.MONDAY) { - return Time.MONDAY; + Time.MONDAY } else { - return Time.SUNDAY; + Time.SUNDAY } } @@ -605,55 +585,50 @@ public class Utils { * * @return the first day of week as a java.util.Calendar constant */ - public static int getFirstDayOfWeekAsCalendar(Context context) { - return convertDayOfWeekFromTimeToCalendar(getFirstDayOfWeek(context)); + @JvmStatic fun getFirstDayOfWeekAsCalendar(context: Context?): Int { + return convertDayOfWeekFromTimeToCalendar(getFirstDayOfWeek(context)) } /** * Converts the day of the week from android.text.format.Time to java.util.Calendar */ - public static int convertDayOfWeekFromTimeToCalendar(int timeDayOfWeek) { - switch (timeDayOfWeek) { - case Time.MONDAY: - return Calendar.MONDAY; - case Time.TUESDAY: - return Calendar.TUESDAY; - case Time.WEDNESDAY: - return Calendar.WEDNESDAY; - case Time.THURSDAY: - return Calendar.THURSDAY; - case Time.FRIDAY: - return Calendar.FRIDAY; - case Time.SATURDAY: - return Calendar.SATURDAY; - case Time.SUNDAY: - return Calendar.SUNDAY; - default: - throw new IllegalArgumentException("Argument must be between Time.SUNDAY and " + - "Time.SATURDAY"); + @JvmStatic fun convertDayOfWeekFromTimeToCalendar(timeDayOfWeek: Int): Int { + return when (timeDayOfWeek) { + Time.MONDAY -> Calendar.MONDAY + Time.TUESDAY -> Calendar.TUESDAY + Time.WEDNESDAY -> Calendar.WEDNESDAY + Time.THURSDAY -> Calendar.THURSDAY + Time.FRIDAY -> Calendar.FRIDAY + Time.SATURDAY -> Calendar.SATURDAY + Time.SUNDAY -> Calendar.SUNDAY + else -> throw IllegalArgumentException( + "Argument must be between Time.SUNDAY and " + + "Time.SATURDAY" + ) } } /** * @return true when week number should be shown. */ - public static boolean getShowWeekNumber(Context context) { - final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getBoolean( - GeneralPreferences.KEY_SHOW_WEEK_NUM, GeneralPreferences.DEFAULT_SHOW_WEEK_NUM); + @JvmStatic fun getShowWeekNumber(context: Context?): Boolean { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + return prefs?.getBoolean( + GeneralPreferences.KEY_SHOW_WEEK_NUM, GeneralPreferences.DEFAULT_SHOW_WEEK_NUM + ) as Boolean } /** * @return true when declined events should be hidden. */ - public static boolean getHideDeclinedEvents(Context context) { - final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getBoolean(GeneralPreferences.KEY_HIDE_DECLINED, false); + @JvmStatic fun getHideDeclinedEvents(context: Context?): Boolean { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + return prefs?.getBoolean(GeneralPreferences.KEY_HIDE_DECLINED, false) as Boolean } - public static int getDaysPerWeek(Context context) { - final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getInt(GeneralPreferences.KEY_DAYS_PER_WEEK, 7); + @JvmStatic fun getDaysPerWeek(context: Context?): Int { + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(context) + return prefs?.getInt(GeneralPreferences.KEY_DAYS_PER_WEEK, 7) as Int } /** @@ -663,10 +638,10 @@ public class Utils { * @param firstDayOfWeek the first day of week in android.text.format.Time * @return true if the column is Saturday position */ - public static boolean isSaturday(int column, int firstDayOfWeek) { - return (firstDayOfWeek == Time.SUNDAY && column == 6) - || (firstDayOfWeek == Time.MONDAY && column == 5) - || (firstDayOfWeek == Time.SATURDAY && column == 0); + @JvmStatic fun isSaturday(column: Int, firstDayOfWeek: Int): Boolean { + return (firstDayOfWeek == Time.SUNDAY && column == 6 || + firstDayOfWeek == Time.MONDAY && column == 5 || + firstDayOfWeek == Time.SATURDAY && column == 0) } /** @@ -676,10 +651,10 @@ public class Utils { * @param firstDayOfWeek the first day of week in android.text.format.Time * @return true if the column is Sunday position */ - public static boolean isSunday(int column, int firstDayOfWeek) { - return (firstDayOfWeek == Time.SUNDAY && column == 0) - || (firstDayOfWeek == Time.MONDAY && column == 6) - || (firstDayOfWeek == Time.SATURDAY && column == 1); + @JvmStatic fun isSunday(column: Int, firstDayOfWeek: Int): Boolean { + return (firstDayOfWeek == Time.SUNDAY && column == 0 || + firstDayOfWeek == Time.MONDAY && column == 6 || + firstDayOfWeek == Time.SATURDAY && column == 1) } /** @@ -690,24 +665,26 @@ public class Utils { * @param utcTime Time to convert, in UTC. * @param tz The time zone to convert this time to. */ - public static long convertAlldayUtcToLocal(Time recycle, long utcTime, String tz) { + @JvmStatic fun convertAlldayUtcToLocal(recycle: Time?, utcTime: Long, tz: String): Long { + var recycle: Time? = recycle if (recycle == null) { - recycle = new Time(); + recycle = Time() } - recycle.timezone = Time.TIMEZONE_UTC; - recycle.set(utcTime); - recycle.timezone = tz; - return recycle.normalize(true); + recycle.timezone = Time.TIMEZONE_UTC + recycle.set(utcTime) + recycle.timezone = tz + return recycle.normalize(true) } - public static long convertAlldayLocalToUTC(Time recycle, long localTime, String tz) { + @JvmStatic fun convertAlldayLocalToUTC(recycle: Time?, localTime: Long, tz: String): Long { + var recycle: Time? = recycle if (recycle == null) { - recycle = new Time(); + recycle = Time() } - recycle.timezone = tz; - recycle.set(localTime); - recycle.timezone = Time.TIMEZONE_UTC; - return recycle.normalize(true); + recycle.timezone = tz + recycle.set(localTime) + recycle.timezone = Time.TIMEZONE_UTC + return recycle.normalize(true) } /** @@ -717,29 +694,30 @@ public class Utils { * @param theTime - Time used for calculations (in UTC) * @param tz The time zone to convert this time to. */ - public static long getNextMidnight(Time recycle, long theTime, String tz) { + @JvmStatic fun getNextMidnight(recycle: Time?, theTime: Long, tz: String): Long { + var recycle: Time? = recycle if (recycle == null) { - recycle = new Time(); + recycle = Time() } - recycle.timezone = tz; - recycle.set(theTime); - recycle.monthDay ++; - recycle.hour = 0; - recycle.minute = 0; - recycle.second = 0; - return recycle.normalize(true); + recycle.timezone = tz + recycle.set(theTime) + recycle.monthDay++ + recycle.hour = 0 + recycle.minute = 0 + recycle.second = 0 + return recycle.normalize(true) } - public static void setAllowWeekForDetailView(boolean allowWeekView) { - mAllowWeekForDetailView = allowWeekView; + @JvmStatic fun setAllowWeekForDetailView(allowWeekView: Boolean) { + this.allowWeekForDetailView = allowWeekView } - public static boolean getAllowWeekForDetailView() { - return mAllowWeekForDetailView; + @JvmStatic fun getAllowWeekForDetailView(): Boolean { + return this.allowWeekForDetailView } - public static boolean getConfigBool(Context c, int key) { - return c.getResources().getBoolean(key); + @JvmStatic fun getConfigBool(c: Context, key: Int): Boolean { + return c.getResources().getBoolean(key) } /** @@ -749,70 +727,51 @@ public class Utils { * * @param color */ - public static int getDisplayColorFromColor(int color) { + @JvmStatic fun getDisplayColorFromColor(color: Int): Int { if (!isJellybeanOrLater()) { - return color; + return color } - - float[] hsv = new float[3]; - Color.colorToHSV(color, hsv); - hsv[1] = Math.min(hsv[1] * SATURATION_ADJUST, 1.0f); - hsv[2] = hsv[2] * INTENSITY_ADJUST; - return Color.HSVToColor(hsv); + val hsv = FloatArray(3) + Color.colorToHSV(color, hsv) + hsv[1] = Math.min(hsv[1] * SATURATION_ADJUST, 1.0f) + hsv[2] = hsv[2] * INTENSITY_ADJUST + return Color.HSVToColor(hsv) } // This takes a color and computes what it would look like blended with // white. The result is the color that should be used for declined events. - public static int getDeclinedColorFromColor(int color) { - int bg = 0xffffffff; - int a = DECLINED_EVENT_ALPHA; - int r = (((color & 0x00ff0000) * a) + ((bg & 0x00ff0000) * (0xff - a))) & 0xff000000; - int g = (((color & 0x0000ff00) * a) + ((bg & 0x0000ff00) * (0xff - a))) & 0x00ff0000; - int b = (((color & 0x000000ff) * a) + ((bg & 0x000000ff) * (0xff - a))) & 0x0000ff00; - return (0xff000000) | ((r | g | b) >> 8); - } - - public static void trySyncAndDisableUpgradeReceiver(Context context) { - final PackageManager pm = context.getPackageManager(); - ComponentName upgradeComponent = new ComponentName(context, UpgradeReceiver.class); - if (pm.getComponentEnabledSetting(upgradeComponent) == - PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { + @JvmStatic fun getDeclinedColorFromColor(color: Int): Int { + val bg = -0x1 + val a = DECLINED_EVENT_ALPHA + val r = (color and 0x00ff0000) * a + (bg and 0x00ff0000) * (0xff - a) and -0x1000000 + val g = (color and 0x0000ff00) * a + (bg and 0x0000ff00) * (0xff - a) and 0x00ff0000 + val b = (color and 0x000000ff) * a + (bg and 0x000000ff) * (0xff - a) and 0x0000ff00 + return -0x1000000 or (r or g or b shr 8) + } + + @JvmStatic fun trySyncAndDisableUpgradeReceiver(context: Context?) { + val pm: PackageManager? = context?.getPackageManager() + val upgradeComponent = ComponentName(context as Context, UpgradeReceiver::class.java) + if (pm?.getComponentEnabledSetting(upgradeComponent) === + PackageManager.COMPONENT_ENABLED_STATE_DISABLED + ) { // The upgrade receiver has been disabled, which means this code has been run before, // so no need to sync. - return; + return } - - Bundle extras = new Bundle(); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + val extras = Bundle() + extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true) ContentResolver.requestSync( - null /* no account */, - Calendars.CONTENT_URI.getAuthority(), - extras); + null /* no account */, + Calendars.CONTENT_URI.getAuthority(), + extras + ) // Now unregister the receiver so that we won't continue to sync every time. - pm.setComponentEnabledSetting(upgradeComponent, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); - } - - // A single strand represents one color of events. Events are divided up by - // color to make them convenient to draw. The black strand is special in - // that it holds conflicting events as well as color settings for allday on - // each day. - public static class DNAStrand { - public float[] points; - public int[] allDays; // color for the allday, 0 means no event - int position; - public int color; - int count; - } - - // A segment is a single continuous length of time occupied by a single - // color. Segments should never span multiple days. - private static class DNASegment { - int startMinute; // in minutes since the start of the week - int endMinute; - int color; // Calendar color or black for conflicts - int day; // quick reference to the day this segment is on + pm?.setComponentEnabledSetting( + upgradeComponent, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP + ) } /** @@ -825,16 +784,16 @@ public class Utils { * color. The strands can then be drawn by setting the paint color to each * strand's color and calling drawLines on its set of points. The points are * set up using the following parameters. - *
    - *
  • Events between midnight and WORK_DAY_START_MINUTES are compressed - * into the first 1/8th of the space between top and bottom.
  • - *
  • Events between WORK_DAY_END_MINUTES and the following midnight are - * compressed into the last 1/8th of the space between top and bottom
  • - *
  • Events between WORK_DAY_START_MINUTES and WORK_DAY_END_MINUTES use - * the remaining 3/4ths of the space
  • - *
  • All segments drawn will maintain at least minPixels height, except - * for conflicts in the first or last 1/8th, which may be smaller
  • - *
+ * + * * Events between midnight and WORK_DAY_START_MINUTES are compressed + * into the first 1/8th of the space between top and bottom. + * * Events between WORK_DAY_END_MINUTES and the following midnight are + * compressed into the last 1/8th of the space between top and bottom + * * Events between WORK_DAY_START_MINUTES and WORK_DAY_END_MINUTES use + * the remaining 3/4ths of the space + * * All segments drawn will maintain at least minPixels height, except + * for conflicts in the first or last 1/8th, which may be smaller + * * * @param firstJulianDay The julian day of the first day of events * @param events A list of events sorted by start time @@ -844,285 +803,329 @@ public class Utils { * @param conflictColor the color to use for conflicts * @return */ - public static HashMap createDNAStrands(int firstJulianDay, - ArrayList events, int top, int bottom, int minPixels, int[] dayXs, - Context context) { - + @JvmStatic fun createDNAStrands( + firstJulianDay: Int, + events: ArrayList?, + top: Int, + bottom: Int, + minPixels: Int, + dayXs: IntArray?, + context: Context? + ): HashMap? { if (!mMinutesLoaded) { if (context == null) { - Log.wtf(TAG, "No context and haven't loaded parameters yet! Can't create DNA."); + Log.wtf(TAG, "No context and haven't loaded parameters yet! Can't create DNA.") } - Resources res = context.getResources(); - CONFLICT_COLOR = res.getColor(R.color.month_dna_conflict_time_color); - WORK_DAY_START_MINUTES = res.getInteger(R.integer.work_start_minutes); - WORK_DAY_END_MINUTES = res.getInteger(R.integer.work_end_minutes); - WORK_DAY_END_LENGTH = DAY_IN_MINUTES - WORK_DAY_END_MINUTES; - WORK_DAY_MINUTES = WORK_DAY_END_MINUTES - WORK_DAY_START_MINUTES; - mMinutesLoaded = true; + val res: Resources? = context?.getResources() + CONFLICT_COLOR = res?.getColor(R.color.month_dna_conflict_time_color) as Int + WORK_DAY_START_MINUTES = res?.getInteger(R.integer.work_start_minutes) as Int + WORK_DAY_END_MINUTES = res?.getInteger(R.integer.work_end_minutes) as Int + WORK_DAY_END_LENGTH = DAY_IN_MINUTES - WORK_DAY_END_MINUTES + WORK_DAY_MINUTES = WORK_DAY_END_MINUTES - WORK_DAY_START_MINUTES + mMinutesLoaded = true } - - if (events == null || events.isEmpty() || dayXs == null || dayXs.length < 1 - || bottom - top < 8 || minPixels < 0) { - Log.e(TAG, - "Bad values for createDNAStrands! events:" + events + " dayXs:" - + Arrays.toString(dayXs) + " bot-top:" + (bottom - top) + " minPixels:" - + minPixels); - return null; + if (events == null || events.isEmpty() || dayXs == null || dayXs.size < 1 || + bottom - top < 8 || minPixels < 0) { + Log.e( + TAG, + "Bad values for createDNAStrands! events:" + events + " dayXs:" + + Arrays.toString(dayXs) + " bot-top:" + (bottom - top) + " minPixels:" + + minPixels + ) + return null } - - LinkedList segments = new LinkedList(); - HashMap strands = new HashMap(); + val segments: LinkedList = LinkedList() + val strands: HashMap = HashMap() // add a black strand by default, other colors will get added in // the loop - DNAStrand blackStrand = new DNAStrand(); - blackStrand.color = CONFLICT_COLOR; - strands.put(CONFLICT_COLOR, blackStrand); + val blackStrand = DNAStrand() + blackStrand.color = CONFLICT_COLOR + strands.put(CONFLICT_COLOR, blackStrand) // the min length is the number of minutes that will occupy // MIN_SEGMENT_PIXELS in the 'work day' time slot. This computes the // minutes/pixel * minpx where the number of pixels are 3/4 the total // dna height: 4*(mins/(px * 3/4)) - int minMinutes = minPixels * 4 * WORK_DAY_MINUTES / (3 * (bottom - top)); + val minMinutes = minPixels * 4 * WORK_DAY_MINUTES / (3 * (bottom - top)) // There are slightly fewer than half as many pixels in 1/6 the space, // so round to 2.5x for the min minutes in the non-work area - int minOtherMinutes = minMinutes * 5 / 2; - int lastJulianDay = firstJulianDay + dayXs.length - 1; - - Event event = new Event(); + val minOtherMinutes = minMinutes * 5 / 2 + val lastJulianDay = firstJulianDay + dayXs.size - 1 + val event = Event() // Go through all the events for the week - for (Event currEvent : events) { + for (currEvent in events) { // if this event is outside the weeks range skip it - if (currEvent.endDay < firstJulianDay || currEvent.startDay > lastJulianDay) { - continue; + if (currEvent != null && + (currEvent.endDay < firstJulianDay || currEvent.startDay > lastJulianDay)) { + continue } - if (currEvent.drawAsAllday()) { - addAllDayToStrands(currEvent, strands, firstJulianDay, dayXs.length); - continue; + if (currEvent?.drawAsAllday() == true) { + addAllDayToStrands(currEvent, strands, firstJulianDay, dayXs.size) + continue } // Copy the event over so we can clip its start and end to our range - currEvent.copyTo(event); + currEvent?.copyTo(event) if (event.startDay < firstJulianDay) { - event.startDay = firstJulianDay; - event.startTime = 0; + event.startDay = firstJulianDay + event.startTime = 0 } // If it starts after the work day make sure the start is at least // minPixels from midnight if (event.startTime > DAY_IN_MINUTES - minOtherMinutes) { - event.startTime = DAY_IN_MINUTES - minOtherMinutes; + event.startTime = DAY_IN_MINUTES - minOtherMinutes } if (event.endDay > lastJulianDay) { - event.endDay = lastJulianDay; - event.endTime = DAY_IN_MINUTES - 1; + event.endDay = lastJulianDay + event.endTime = DAY_IN_MINUTES - 1 } // If the end time is before the work day make sure it ends at least // minPixels after midnight if (event.endTime < minOtherMinutes) { - event.endTime = minOtherMinutes; + event.endTime = minOtherMinutes } // If the start and end are on the same day make sure they are at // least minPixels apart. This only needs to be done for times // outside the work day as the min distance for within the work day // is enforced in the segment code. - if (event.startDay == event.endDay && - event.endTime - event.startTime < minOtherMinutes) { + if (event.startDay === event.endDay && + event.endTime - event.startTime < minOtherMinutes + ) { // If it's less than minPixels in an area before the work // day if (event.startTime < WORK_DAY_START_MINUTES) { // extend the end to the first easy guarantee that it's // minPixels - event.endTime = Math.min(event.startTime + minOtherMinutes, - WORK_DAY_START_MINUTES + minMinutes); + event.endTime = Math.min( + event.startTime + minOtherMinutes, + WORK_DAY_START_MINUTES + minMinutes + ) // if it's in the area after the work day } else if (event.endTime > WORK_DAY_END_MINUTES) { // First try shifting the end but not past midnight - event.endTime = Math.min(event.endTime + minOtherMinutes, DAY_IN_MINUTES - 1); + event.endTime = Math.min(event.endTime + minOtherMinutes, DAY_IN_MINUTES - 1) // if it's still too small move the start back if (event.endTime - event.startTime < minOtherMinutes) { - event.startTime = event.endTime - minOtherMinutes; + event.startTime = event.endTime - minOtherMinutes } } } // This handles adding the first segment - if (segments.size() == 0) { - addNewSegment(segments, event, strands, firstJulianDay, 0, minMinutes); - continue; + if (segments.size == 0) { + addNewSegment(segments, event, strands, firstJulianDay, 0, minMinutes) + continue } // Now compare our current start time to the end time of the last // segment in the list - DNASegment lastSegment = segments.getLast(); - int startMinute = (event.startDay - firstJulianDay) * DAY_IN_MINUTES + event.startTime; - int endMinute = Math.max((event.endDay - firstJulianDay) * DAY_IN_MINUTES - + event.endTime, startMinute + minMinutes); - + val lastSegment: DNASegment = segments.getLast() + var startMinute: Int = + (event.startDay - firstJulianDay) * DAY_IN_MINUTES + event.startTime + var endMinute: Int = Math.max( + (event.endDay - firstJulianDay) * DAY_IN_MINUTES + + event.endTime, startMinute + minMinutes + ) if (startMinute < 0) { - startMinute = 0; + startMinute = 0 } if (endMinute >= WEEK_IN_MINUTES) { - endMinute = WEEK_IN_MINUTES - 1; + endMinute = WEEK_IN_MINUTES - 1 } // If we start before the last segment in the list ends we need to // start going through the list as this may conflict with other // events if (startMinute < lastSegment.endMinute) { - int i = segments.size(); + var i: Int = segments.size // find the last segment this event intersects with - while (--i >= 0 && endMinute < segments.get(i).startMinute); + while (--i >= 0 && endMinute < segments.get(i).startMinute) {} - DNASegment currSegment; + var currSegment: DNASegment = DNASegment() // for each segment this event intersects with - for (; i >= 0 && startMinute <= (currSegment = segments.get(i)).endMinute; i--) { + while (i >= 0 && startMinute <= segments.get(i) + .also { currSegment = it }.endMinute) { + // if the segment is already a conflict ignore it if (currSegment.color == CONFLICT_COLOR) { - continue; + i-- + continue } // if the event ends before the segment and wouldn't create // a segment that is too small split off the right side if (endMinute < currSegment.endMinute - minMinutes) { - DNASegment rhs = new DNASegment(); - rhs.endMinute = currSegment.endMinute; - rhs.color = currSegment.color; - rhs.startMinute = endMinute + 1; - rhs.day = currSegment.day; - currSegment.endMinute = endMinute; - segments.add(i + 1, rhs); - strands.get(rhs.color).count++; + val rhs = DNASegment() + rhs.endMinute = currSegment.endMinute + rhs.color = currSegment.color + rhs.startMinute = endMinute + 1 + rhs.day = currSegment.day + currSegment.endMinute = endMinute + segments.add(i + 1, rhs) + // Equivalent to strands.get(rhs.color)?.count++ + // but there is no null safe invocation for ++ + strands.get(rhs.color)?.count = strands.get(rhs.color)?.count?.inc() as Int if (DEBUG) { - Log.d(TAG, "Added rhs, curr:" + currSegment.toString() + " i:" - + segments.get(i).toString()); + Log.d( + TAG, "Added rhs, curr:" + currSegment.toString() + " i:" + + segments.get(i).toString() + ) } } // if the event starts after the segment and wouldn't create // a segment that is too small split off the left side if (startMinute > currSegment.startMinute + minMinutes) { - DNASegment lhs = new DNASegment(); - lhs.startMinute = currSegment.startMinute; - lhs.color = currSegment.color; - lhs.endMinute = startMinute - 1; - lhs.day = currSegment.day; - currSegment.startMinute = startMinute; + val lhs = DNASegment() + lhs.startMinute = currSegment.startMinute + lhs.color = currSegment.color + lhs.endMinute = startMinute - 1 + lhs.day = currSegment.day + currSegment.startMinute = startMinute // increment i so that we are at the right position when // referencing the segments to the right and left of the // current segment. - segments.add(i++, lhs); - strands.get(lhs.color).count++; + segments.add(i++, lhs) + strands.get(lhs.color)?.count = strands.get(lhs.color)?.count?.inc() as Int if (DEBUG) { - Log.d(TAG, "Added lhs, curr:" + currSegment.toString() + " i:" - + segments.get(i).toString()); + Log.d( + TAG, "Added lhs, curr:" + currSegment.toString() + " i:" + + segments.get(i).toString() + ) } } // if the right side is black merge this with the segment to // the right if they're on the same day and overlap - if (i + 1 < segments.size()) { - DNASegment rhs = segments.get(i + 1); - if (rhs.color == CONFLICT_COLOR && currSegment.day == rhs.day - && rhs.startMinute <= currSegment.endMinute + 1) { - rhs.startMinute = Math.min(currSegment.startMinute, rhs.startMinute); - segments.remove(currSegment); - strands.get(currSegment.color).count--; + if (i + 1 < segments.size) { + val rhs: DNASegment = segments.get(i + 1) + if (rhs.color == CONFLICT_COLOR && currSegment.day == rhs.day && + rhs.startMinute <= currSegment.endMinute + 1) { + rhs.startMinute = Math.min(currSegment.startMinute, rhs.startMinute) + segments.remove(currSegment) + strands.get(currSegment.color)?.count = + strands.get(currSegment.color)?.count?.dec() as Int // point at the new current segment - currSegment = rhs; + currSegment = rhs } } // if the left side is black merge this with the segment to // the left if they're on the same day and overlap if (i - 1 >= 0) { - DNASegment lhs = segments.get(i - 1); - if (lhs.color == CONFLICT_COLOR && currSegment.day == lhs.day - && lhs.endMinute >= currSegment.startMinute - 1) { - lhs.endMinute = Math.max(currSegment.endMinute, lhs.endMinute); - segments.remove(currSegment); - strands.get(currSegment.color).count--; + val lhs: DNASegment = segments.get(i - 1) + if (lhs.color == CONFLICT_COLOR && currSegment.day == lhs.day && + lhs.endMinute >= currSegment.startMinute - 1) { + lhs.endMinute = Math.max(currSegment.endMinute, lhs.endMinute) + segments.remove(currSegment) + strands.get(currSegment.color)?.count = + strands.get(currSegment.color)?.count?.dec() as Int // point at the new current segment - currSegment = lhs; + currSegment = lhs // point i at the new current segment in case new // code is added - i--; + i-- } } // if we're still not black, decrement the count for the // color being removed, change this to black, and increment // the black count if (currSegment.color != CONFLICT_COLOR) { - strands.get(currSegment.color).count--; - currSegment.color = CONFLICT_COLOR; - strands.get(CONFLICT_COLOR).count++; + strands.get(currSegment.color)?.count = + strands.get(currSegment.color)?.count?.dec() as Int + currSegment.color = CONFLICT_COLOR + strands.get(CONFLICT_COLOR)?.count = + strands.get(CONFLICT_COLOR)?.count?.inc() as Int } + i-- } - } // If this event extends beyond the last segment add a new segment if (endMinute > lastSegment.endMinute) { - addNewSegment(segments, event, strands, firstJulianDay, lastSegment.endMinute, - minMinutes); + addNewSegment( + segments, event, strands, firstJulianDay, lastSegment.endMinute, + minMinutes + ) } } - weaveDNAStrands(segments, firstJulianDay, strands, top, bottom, dayXs); - return strands; + weaveDNAStrands(segments, firstJulianDay, strands, top, bottom, dayXs) + return strands } // This figures out allDay colors as allDay events are found - private static void addAllDayToStrands(Event event, HashMap strands, - int firstJulianDay, int numDays) { - DNAStrand strand = getOrCreateStrand(strands, CONFLICT_COLOR); + private fun addAllDayToStrands( + event: Event?, + strands: HashMap, + firstJulianDay: Int, + numDays: Int + ) { + val strand = getOrCreateStrand(strands, CONFLICT_COLOR) // if we haven't initialized the allDay portion create it now - if (strand.allDays == null) { - strand.allDays = new int[numDays]; + if (strand?.allDays == null) { + strand?.allDays = IntArray(numDays) } // For each day this event is on update the color - int end = Math.min(event.endDay - firstJulianDay, numDays - 1); - for (int i = Math.max(event.startDay - firstJulianDay, 0); i <= end; i++) { - if (strand.allDays[i] != 0) { + val end: Int = Math.min((event?.endDay ?: 0) - firstJulianDay, numDays - 1) + for (i in Math.max((event?.startDay ?: 0) - firstJulianDay, 0)..end) { + if (strand?.allDays!![i] != 0) { // if this day already had a color, it is now a conflict - strand.allDays[i] = CONFLICT_COLOR; + strand?.allDays!![i] = CONFLICT_COLOR } else { // else it's just the color of the event - strand.allDays[i] = event.color; + strand?.allDays!![i] = event?.color as Int } } } // This processes all the segments, sorts them by color, and generates a // list of points to draw - private static void weaveDNAStrands(LinkedList segments, int firstJulianDay, - HashMap strands, int top, int bottom, int[] dayXs) { + private fun weaveDNAStrands( + segments: LinkedList, + firstJulianDay: Int, + strands: HashMap, + top: Int, + bottom: Int, + dayXs: IntArray + ) { // First, get rid of any colors that ended up with no segments - Iterator strandIterator = strands.values().iterator(); + val strandIterator = strands.values.iterator() while (strandIterator.hasNext()) { - DNAStrand strand = strandIterator.next(); - if (strand.count < 1 && strand.allDays == null) { - strandIterator.remove(); - continue; + val strand = strandIterator.next() + if (strand?.count < 1 && strand.allDays == null) { + strandIterator.remove() + continue } - strand.points = new float[strand.count * 4]; - strand.position = 0; + strand.points = FloatArray(strand.count * 4) + strand.position = 0 } // Go through each segment and compute its points - for (DNASegment segment : segments) { + for (segment in segments) { // Add the points to the strand of that color - DNAStrand strand = strands.get(segment.color); - int dayIndex = segment.day - firstJulianDay; - int dayStartMinute = segment.startMinute % DAY_IN_MINUTES; - int dayEndMinute = segment.endMinute % DAY_IN_MINUTES; - int height = bottom - top; - int workDayHeight = height * 3 / 4; - int remainderHeight = (height - workDayHeight) / 2; - - int x = dayXs[dayIndex]; - int y0 = 0; - int y1 = 0; - - y0 = top + getPixelOffsetFromMinutes(dayStartMinute, workDayHeight, remainderHeight); - y1 = top + getPixelOffsetFromMinutes(dayEndMinute, workDayHeight, remainderHeight); + val strand: DNAStrand? = strands.get(segment.color) + val dayIndex = segment.day - firstJulianDay + val dayStartMinute = segment.startMinute % DAY_IN_MINUTES + val dayEndMinute = segment.endMinute % DAY_IN_MINUTES + val height = bottom - top + val workDayHeight = height * 3 / 4 + val remainderHeight = (height - workDayHeight) / 2 + val x = dayXs[dayIndex] + var y0 = 0 + var y1 = 0 + y0 = top + getPixelOffsetFromMinutes(dayStartMinute, workDayHeight, remainderHeight) + y1 = top + getPixelOffsetFromMinutes(dayEndMinute, workDayHeight, remainderHeight) if (DEBUG) { - Log.d(TAG, "Adding " + Integer.toHexString(segment.color) + " at x,y0,y1: " + x - + " " + y0 + " " + y1 + " for " + dayStartMinute + " " + dayEndMinute); + Log.d( + TAG, + "Adding " + Integer.toHexString(segment.color).toString() + " at x,y0,y1: " + x + .toString() + " " + y0.toString() + " " + y1.toString() + + " for " + dayStartMinute.toString() + " " + dayEndMinute + ) } - strand.points[strand.position++] = x; - strand.points[strand.position++] = y0; - strand.points[strand.position++] = x; - strand.points[strand.position++] = y1; + strand?.points!![strand?.position] = x.toFloat() + strand?.position = strand?.position?.inc() as Int + + strand?.points!![strand?.position] = y0.toFloat() + strand?.position = strand?.position?.inc() as Int + + strand?.points!![strand?.position] = x.toFloat() + strand?.position = strand?.position.inc() as Int + + strand?.points!![strand?.position] = y1.toFloat() + strand?.position = strand?.position.inc() as Int } } @@ -1130,88 +1133,99 @@ public class Utils { * Compute a pixel offset from the top for a given minute from the work day * height and the height of the top area. */ - private static int getPixelOffsetFromMinutes(int minute, int workDayHeight, - int remainderHeight) { - int y; + private fun getPixelOffsetFromMinutes( + minute: Int, + workDayHeight: Int, + remainderHeight: Int + ): Int { + val y: Int if (minute < WORK_DAY_START_MINUTES) { - y = minute * remainderHeight / WORK_DAY_START_MINUTES; + y = minute * remainderHeight / WORK_DAY_START_MINUTES } else if (minute < WORK_DAY_END_MINUTES) { - y = remainderHeight + (minute - WORK_DAY_START_MINUTES) * workDayHeight - / WORK_DAY_MINUTES; + y = remainderHeight + (minute - WORK_DAY_START_MINUTES) * + workDayHeight / WORK_DAY_MINUTES } else { - y = remainderHeight + workDayHeight + (minute - WORK_DAY_END_MINUTES) * remainderHeight - / WORK_DAY_END_LENGTH; + y = remainderHeight + workDayHeight + + (minute - WORK_DAY_END_MINUTES) * remainderHeight / WORK_DAY_END_LENGTH } - return y; + return y } /** * Add a new segment based on the event provided. This will handle splitting * segments across day boundaries and ensures a minimum size for segments. */ - private static void addNewSegment(LinkedList segments, Event event, - HashMap strands, int firstJulianDay, int minStart, int minMinutes) { + private fun addNewSegment( + segments: LinkedList, + event: Event, + strands: HashMap, + firstJulianDay: Int, + minStart: Int, + minMinutes: Int + ) { + var event: Event = event + var minStart = minStart if (event.startDay > event.endDay) { - Log.wtf(TAG, "Event starts after it ends: " + event.toString()); + Log.wtf(TAG, "Event starts after it ends: " + event.toString()) } // If this is a multiday event split it up by day - if (event.startDay != event.endDay) { - Event lhs = new Event(); - lhs.color = event.color; - lhs.startDay = event.startDay; + if (event.startDay !== event.endDay) { + val lhs = Event() + lhs.color = event.color + lhs.startDay = event.startDay // the first day we want the start time to be the actual start time - lhs.startTime = event.startTime; - lhs.endDay = lhs.startDay; - lhs.endTime = DAY_IN_MINUTES - 1; + lhs.startTime = event.startTime + lhs.endDay = lhs.startDay + lhs.endTime = DAY_IN_MINUTES - 1 // Nearly recursive iteration! - while (lhs.startDay != event.endDay) { - addNewSegment(segments, lhs, strands, firstJulianDay, minStart, minMinutes); + while (lhs.startDay !== event.endDay) { + addNewSegment(segments, lhs, strands, firstJulianDay, minStart, minMinutes) // The days in between are all day, even though that shouldn't // actually happen due to the allday filtering - lhs.startDay++; - lhs.endDay = lhs.startDay; - lhs.startTime = 0; - minStart = 0; + lhs.startDay++ + lhs.endDay = lhs.startDay + lhs.startTime = 0 + minStart = 0 } // The last day we want the end time to be the actual end time - lhs.endTime = event.endTime; - event = lhs; + lhs.endTime = event.endTime + event = lhs } // Create the new segment and compute its fields - DNASegment segment = new DNASegment(); - int dayOffset = (event.startDay - firstJulianDay) * DAY_IN_MINUTES; - int endOfDay = dayOffset + DAY_IN_MINUTES - 1; + val segment = DNASegment() + val dayOffset: Int = (event.startDay - firstJulianDay) * DAY_IN_MINUTES + val endOfDay = dayOffset + DAY_IN_MINUTES - 1 // clip the start if needed - segment.startMinute = Math.max(dayOffset + event.startTime, minStart); + segment.startMinute = Math.max(dayOffset + event.startTime, minStart) // and extend the end if it's too small, but not beyond the end of the // day - int minEnd = Math.min(segment.startMinute + minMinutes, endOfDay); - segment.endMinute = Math.max(dayOffset + event.endTime, minEnd); + val minEnd: Int = Math.min(segment.startMinute + minMinutes, endOfDay) + segment.endMinute = Math.max(dayOffset + event.endTime, minEnd) if (segment.endMinute > endOfDay) { - segment.endMinute = endOfDay; + segment.endMinute = endOfDay } - - segment.color = event.color; - segment.day = event.startDay; - segments.add(segment); + segment.color = event.color + segment.day = event.startDay + segments.add(segment) // increment the count for the correct color or add a new strand if we // don't have that color yet - DNAStrand strand = getOrCreateStrand(strands, segment.color); - strand.count++; + val strand = getOrCreateStrand(strands, segment.color) + strand?.count + strand?.count = strand?.count?.inc() as Int } /** * Try to get a strand of the given color. Create it if it doesn't exist. */ - private static DNAStrand getOrCreateStrand(HashMap strands, int color) { - DNAStrand strand = strands.get(color); + private fun getOrCreateStrand(strands: HashMap, color: Int): DNAStrand? { + var strand: DNAStrand? = strands.get(color) if (strand == null) { - strand = new DNAStrand(); - strand.color = color; - strand.count = 0; - strands.put(strand.color, strand); + strand = DNAStrand() + strand?.color = color + strand?.count = 0 + strands?.put(strand?.color, strand) } - return strand; + return strand } /** @@ -1219,12 +1233,12 @@ public class Utils { * * @param context */ - public static void returnToCalendarHome(Context context) { - Intent launchIntent = new Intent(context, AllInOneActivity.class); - launchIntent.setAction(Intent.ACTION_DEFAULT); - launchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - launchIntent.putExtra(INTENT_KEY_HOME, true); - context.startActivity(launchIntent); + @JvmStatic fun returnToCalendarHome(context: Context) { + val launchIntent = Intent(context, AllInOneActivity::class.java) + launchIntent.setAction(Intent.ACTION_DEFAULT) + launchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + launchIntent.putExtra(INTENT_KEY_HOME, true) + context.startActivity(launchIntent) } /** @@ -1234,23 +1248,24 @@ public class Utils { * @param millisSinceEpoch * @return */ - public static int getWeekNumberFromTime(long millisSinceEpoch, Context context) { - Time weekTime = new Time(getTimeZone(context, null)); - weekTime.set(millisSinceEpoch); - weekTime.normalize(true); - int firstDayOfWeek = getFirstDayOfWeek(context); + @JvmStatic fun getWeekNumberFromTime(millisSinceEpoch: Long, context: Context?): Int { + val weekTime = Time(getTimeZone(context, null)) + weekTime.set(millisSinceEpoch) + weekTime.normalize(true) + val firstDayOfWeek = getFirstDayOfWeek(context) // if the date is on Saturday or Sunday and the start of the week // isn't Monday we may need to shift the date to be in the correct // week - if (weekTime.weekDay == Time.SUNDAY - && (firstDayOfWeek == Time.SUNDAY || firstDayOfWeek == Time.SATURDAY)) { - weekTime.monthDay++; - weekTime.normalize(true); - } else if (weekTime.weekDay == Time.SATURDAY && firstDayOfWeek == Time.SATURDAY) { - weekTime.monthDay += 2; - weekTime.normalize(true); + if (weekTime.weekDay === Time.SUNDAY && + (firstDayOfWeek == Time.SUNDAY || firstDayOfWeek == Time.SATURDAY) + ) { + weekTime.monthDay++ + weekTime.normalize(true) + } else if (weekTime.weekDay === Time.SATURDAY && firstDayOfWeek == Time.SATURDAY) { + weekTime.monthDay += 2 + weekTime.normalize(true) } - return weekTime.getWeekNumber(); + return weekTime.getWeekNumber() } /** @@ -1261,182 +1276,225 @@ public class Utils { * @param todayJulianDay The julian day for today's date * @param millis A utc millis since epoch time that falls on julian day * @param context The calling context, used to get the timezone and do the - * formatting + * formatting * @return */ - public static String getDayOfWeekString(int julianDay, int todayJulianDay, long millis, - Context context) { - getTimeZone(context, null); - int flags = DateUtils.FORMAT_SHOW_WEEKDAY; - String dayViewText; - if (julianDay == todayJulianDay) { - dayViewText = context.getString(R.string.agenda_today, - mTZUtils.formatDateRange(context, millis, millis, flags).toString()); + @JvmStatic fun getDayOfWeekString( + julianDay: Int, + todayJulianDay: Int, + millis: Long, + context: Context + ): String { + getTimeZone(context, null) + val flags: Int = DateUtils.FORMAT_SHOW_WEEKDAY + var dayViewText: String + dayViewText = if (julianDay == todayJulianDay) { + context.getString( + R.string.agenda_today, + mTZUtils?.formatDateRange(context, millis, millis, flags) + .toString() + ) } else if (julianDay == todayJulianDay - 1) { - dayViewText = context.getString(R.string.agenda_yesterday, - mTZUtils.formatDateRange(context, millis, millis, flags).toString()); + context.getString( + R.string.agenda_yesterday, + mTZUtils?.formatDateRange(context, millis, millis, flags) + .toString() + ) } else if (julianDay == todayJulianDay + 1) { - dayViewText = context.getString(R.string.agenda_tomorrow, - mTZUtils.formatDateRange(context, millis, millis, flags).toString()); + context.getString( + R.string.agenda_tomorrow, + mTZUtils?.formatDateRange(context, millis, millis, flags) + .toString() + ) } else { - dayViewText = mTZUtils.formatDateRange(context, millis, millis, flags).toString(); + mTZUtils?.formatDateRange(context, millis, millis, flags) + .toString() } - dayViewText = dayViewText.toUpperCase(); - return dayViewText; + dayViewText = dayViewText.toUpperCase() + return dayViewText } // Calculate the time until midnight + 1 second and set the handler to // do run the runnable - public static void setMidnightUpdater(Handler h, Runnable r, String timezone) { + @JvmStatic fun setMidnightUpdater(h: Handler?, r: Runnable?, timezone: String?) { if (h == null || r == null || timezone == null) { - return; + return } - long now = System.currentTimeMillis(); - Time time = new Time(timezone); - time.set(now); - long runInMillis = (24 * 3600 - time.hour * 3600 - time.minute * 60 - - time.second + 1) * 1000; - h.removeCallbacks(r); - h.postDelayed(r, runInMillis); + val now: Long = System.currentTimeMillis() + val time = Time(timezone) + time.set(now) + val runInMillis: Long = ((24 * 3600 - time.hour * 3600 - time.minute * 60 - + time.second + 1) * 1000).toLong() + h.removeCallbacks(r) + h.postDelayed(r, runInMillis) } // Stop the midnight update thread - public static void resetMidnightUpdater(Handler h, Runnable r) { + @JvmStatic fun resetMidnightUpdater(h: Handler?, r: Runnable?) { if (h == null || r == null) { - return; + return } - h.removeCallbacks(r); + h.removeCallbacks(r) } /** * Returns a string description of the specified time interval. */ - public static String getDisplayedDatetime(long startMillis, long endMillis, long currentMillis, - String localTimezone, boolean allDay, Context context) { + @JvmStatic fun getDisplayedDatetime( + startMillis: Long, + endMillis: Long, + currentMillis: Long, + localTimezone: String, + allDay: Boolean, + context: Context + ): String? { // Configure date/time formatting. - int flagsDate = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; - int flagsTime = DateUtils.FORMAT_SHOW_TIME; + val flagsDate: Int = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_WEEKDAY + var flagsTime: Int = DateUtils.FORMAT_SHOW_TIME if (DateFormat.is24HourFormat(context)) { - flagsTime |= DateUtils.FORMAT_24HOUR; + flagsTime = flagsTime or DateUtils.FORMAT_24HOUR } - - Time currentTime = new Time(localTimezone); - currentTime.set(currentMillis); - Resources resources = context.getResources(); - String datetimeString = null; + val currentTime = Time(localTimezone) + currentTime.set(currentMillis) + val resources: Resources = context.getResources() + var datetimeString: String? = null if (allDay) { // All day events require special timezone adjustment. - long localStartMillis = convertAlldayUtcToLocal(null, startMillis, localTimezone); - long localEndMillis = convertAlldayUtcToLocal(null, endMillis, localTimezone); + val localStartMillis = convertAlldayUtcToLocal(null, startMillis, localTimezone) + val localEndMillis = convertAlldayUtcToLocal(null, endMillis, localTimezone) if (singleDayEvent(localStartMillis, localEndMillis, currentTime.gmtoff)) { // If possible, use "Today" or "Tomorrow" instead of a full date string. - int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), - localStartMillis, currentMillis, currentTime.gmtoff); + val todayOrTomorrow = isTodayOrTomorrow( + context.getResources(), + localStartMillis, currentMillis, currentTime.gmtoff + ) if (TODAY == todayOrTomorrow) { - datetimeString = resources.getString(R.string.today); + datetimeString = resources.getString(R.string.today) } else if (TOMORROW == todayOrTomorrow) { - datetimeString = resources.getString(R.string.tomorrow); + datetimeString = resources.getString(R.string.tomorrow) } } if (datetimeString == null) { // For multi-day allday events or single-day all-day events that are not // today or tomorrow, use framework formatter. - Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault()); - datetimeString = DateUtils.formatDateRange(context, f, startMillis, - endMillis, flagsDate, Time.TIMEZONE_UTC).toString(); + val f = Formatter(StringBuilder(50), Locale.getDefault()) + datetimeString = DateUtils.formatDateRange( + context, f, startMillis, + endMillis, flagsDate, Time.TIMEZONE_UTC + ).toString() } } else { - if (singleDayEvent(startMillis, endMillis, currentTime.gmtoff)) { + datetimeString = if (singleDayEvent(startMillis, endMillis, currentTime.gmtoff)) { // Format the time. - String timeString = Utils.formatDateRange(context, startMillis, endMillis, - flagsTime); + val timeString = formatDateRange( + context, startMillis, endMillis, + flagsTime + ) // If possible, use "Today" or "Tomorrow" instead of a full date string. - int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), startMillis, - currentMillis, currentTime.gmtoff); + val todayOrTomorrow = isTodayOrTomorrow( + context.getResources(), startMillis, + currentMillis, currentTime.gmtoff + ) if (TODAY == todayOrTomorrow) { // Example: "Today at 1:00pm - 2:00 pm" - datetimeString = resources.getString(R.string.today_at_time_fmt, - timeString); + resources.getString( + R.string.today_at_time_fmt, + timeString + ) } else if (TOMORROW == todayOrTomorrow) { // Example: "Tomorrow at 1:00pm - 2:00 pm" - datetimeString = resources.getString(R.string.tomorrow_at_time_fmt, - timeString); + resources.getString( + R.string.tomorrow_at_time_fmt, + timeString + ) } else { // Format the full date. Example: "Thursday, April 12, 1:00pm - 2:00pm" - String dateString = Utils.formatDateRange(context, startMillis, endMillis, - flagsDate); - datetimeString = resources.getString(R.string.date_time_fmt, dateString, - timeString); + val dateString = formatDateRange( + context, startMillis, endMillis, + flagsDate + ) + resources.getString( + R.string.date_time_fmt, dateString, + timeString + ) } } else { // For multiday events, shorten day/month names. // Example format: "Fri Apr 6, 5:00pm - Sun, Apr 8, 6:00pm" - int flagsDatetime = flagsDate | flagsTime | DateUtils.FORMAT_ABBREV_MONTH | - DateUtils.FORMAT_ABBREV_WEEKDAY; - datetimeString = Utils.formatDateRange(context, startMillis, endMillis, - flagsDatetime); + val flagsDatetime = flagsDate or flagsTime or DateUtils.FORMAT_ABBREV_MONTH or + DateUtils.FORMAT_ABBREV_WEEKDAY + formatDateRange( + context, startMillis, endMillis, + flagsDatetime + ) } } - return datetimeString; + return datetimeString } /** * Returns the timezone to display in the event info, if the local timezone is different * from the event timezone. Otherwise returns null. */ - public static String getDisplayedTimezone(long startMillis, String localTimezone, - String eventTimezone) { - String tzDisplay = null; + @JvmStatic fun getDisplayedTimezone( + startMillis: Long, + localTimezone: String?, + eventTimezone: String? + ): String? { + var tzDisplay: String? = null if (!TextUtils.equals(localTimezone, eventTimezone)) { // Figure out if this is in DST - TimeZone tz = TimeZone.getTimeZone(localTimezone); - if (tz == null || tz.getID().equals("GMT")) { - tzDisplay = localTimezone; + val tz: TimeZone = TimeZone.getTimeZone(localTimezone) + tzDisplay = if (tz == null || tz.getID().equals("GMT")) { + localTimezone } else { - Time startTime = new Time(localTimezone); - startTime.set(startMillis); - tzDisplay = tz.getDisplayName(startTime.isDst != 0, TimeZone.SHORT); + val startTime = Time(localTimezone) + startTime.set(startMillis) + tz.getDisplayName(startTime.isDst !== 0, TimeZone.SHORT) } } - return tzDisplay; + return tzDisplay } /** * Returns whether the specified time interval is in a single day. */ - private static boolean singleDayEvent(long startMillis, long endMillis, long localGmtOffset) { + private fun singleDayEvent(startMillis: Long, endMillis: Long, localGmtOffset: Long): Boolean { if (startMillis == endMillis) { - return true; + return true } // An event ending at midnight should still be a single-day event, so check // time end-1. - int startDay = Time.getJulianDay(startMillis, localGmtOffset); - int endDay = Time.getJulianDay(endMillis - 1, localGmtOffset); - return startDay == endDay; + val startDay: Int = Time.getJulianDay(startMillis, localGmtOffset) + val endDay: Int = Time.getJulianDay(endMillis - 1, localGmtOffset) + return startDay == endDay } // Using int constants as a return value instead of an enum to minimize resources. - private static final int TODAY = 1; - private static final int TOMORROW = 2; - private static final int NONE = 0; + private const val TODAY = 1 + private const val TOMORROW = 2 + private const val NONE = 0 /** * Returns TODAY or TOMORROW if applicable. Otherwise returns NONE. */ - private static int isTodayOrTomorrow(Resources r, long dayMillis, - long currentMillis, long localGmtOffset) { - int startDay = Time.getJulianDay(dayMillis, localGmtOffset); - int currentDay = Time.getJulianDay(currentMillis, localGmtOffset); - - int days = startDay - currentDay; - if (days == 1) { - return TOMORROW; + private fun isTodayOrTomorrow( + r: Resources, + dayMillis: Long, + currentMillis: Long, + localGmtOffset: Long + ): Int { + val startDay: Int = Time.getJulianDay(dayMillis, localGmtOffset) + val currentDay: Int = Time.getJulianDay(currentMillis, localGmtOffset) + val days = startDay - currentDay + return if (days == 1) { + TOMORROW } else if (days == 0) { - return TODAY; + TODAY } else { - return NONE; + NONE } } @@ -1444,23 +1502,23 @@ public class Utils { * Inserts a drawable with today's day into the today's icon in the option menu * @param icon - today's icon from the options menu */ - public static void setTodayIcon(LayerDrawable icon, Context c, String timezone) { - DayOfMonthDrawable today; + @JvmStatic fun setTodayIcon(icon: LayerDrawable, c: Context?, timezone: String?) { + val today: DayOfMonthDrawable // Reuse current drawable if possible - Drawable currentDrawable = icon.findDrawableByLayerId(R.id.today_icon_day); - if (currentDrawable != null && currentDrawable instanceof DayOfMonthDrawable) { - today = (DayOfMonthDrawable)currentDrawable; + val currentDrawable: Drawable? = icon.findDrawableByLayerId(R.id.today_icon_day) + if (currentDrawable != null && currentDrawable is DayOfMonthDrawable) { + today = currentDrawable as DayOfMonthDrawable } else { - today = new DayOfMonthDrawable(c); + today = DayOfMonthDrawable(c as Context) } // Set the day and update the icon - Time now = new Time(timezone); - now.setToNow(); - now.normalize(false); - today.setDayOfMonth(now.monthDay); - icon.mutate(); - icon.setDrawableByLayerId(R.id.today_icon_day, today); + val now = Time(timezone) + now.setToNow() + now.normalize(false) + today.setDayOfMonth(now.monthDay) + icon.mutate() + icon.setDrawableByLayerId(R.id.today_icon_day, today) } /** @@ -1471,29 +1529,49 @@ public class Utils { * @param context * @return a list of quick responses. */ - public static String[] getQuickResponses(Context context) { - String[] s = Utils.getSharedPreference(context, KEY_QUICK_RESPONSES, (String[]) null); - + fun getQuickResponses(context: Context): Array { + var s = getSharedPreference(context, KEY_QUICK_RESPONSES, null as Array?) if (s == null) { - s = context.getResources().getStringArray(R.array.quick_response_defaults); + s = context.getResources().getStringArray(R.array.quick_response_defaults) } - - return s; + return s } /** * Return the app version code. */ - public static String getVersionCode(Context context) { + fun getVersionCode(context: Context): String? { if (sVersion == null) { try { sVersion = context.getPackageManager().getPackageInfo( - context.getPackageName(), 0).versionName; - } catch (PackageManager.NameNotFoundException e) { + context.getPackageName(), 0 + ).versionName + } catch (e: PackageManager.NameNotFoundException) { // Can't find version; just leave it blank. - Log.e(TAG, "Error finding package " + context.getApplicationInfo().packageName); + Log.e(TAG, "Error finding package " + context.getApplicationInfo().packageName) } } - return sVersion; + return sVersion + } + + // A single strand represents one color of events. Events are divided up by + // color to make them convenient to draw. The black strand is special in + // that it holds conflicting events as well as color settings for allday on + // each day. + class DNAStrand { + @JvmField var points: FloatArray? = null + @JvmField var allDays: IntArray? = null // color for the allday, 0 means no event + @JvmField var position = 0 + @JvmField var color = 0 + @JvmField var count = 0 + } + + // A segment is a single continuous length of time occupied by a single + // color. Segments should never span multiple days. + private class DNASegment { + var startMinute = 0 // in minutes since the start of the week = + var endMinute = 0 + var color = 0 // Calendar color or black for conflicts = + var day = 0 // quick reference to the day this segment is on = } -} +} \ No newline at end of file diff --git a/src/com/android/calendar/month/SimpleWeekView.kt b/src/com/android/calendar/month/SimpleWeekView.kt index 71313d10..d7f25d18 100644 --- a/src/com/android/calendar/month/SimpleWeekView.kt +++ b/src/com/android/calendar/month/SimpleWeekView.kt @@ -428,7 +428,7 @@ open class SimpleWeekView(context: Context) : View(context) { if (hover != null && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) !== 0)) { val millis: Long = hover.toMillis(true) - val date: String = Utils.formatDateRange(context, millis, millis, + val date = Utils.formatDateRange(context, millis, millis, DateUtils.FORMAT_SHOW_DATE) val accessEvent: AccessibilityEvent = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) -- cgit v1.2.3 From 57f31c933d89ef9ca9598f2e2956198ed4318ad2 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 25 Jun 2021 16:07:36 +0000 Subject: AOSP/Calendar - Runtime error fix In SimpleWeekView.kt, there were various occurrences where Int was being casted a Float instead of being parsed with .toFloat(). Without this change, the app would crash upon selecting the "Month" view. Change-Id: I6a89b864562fa380ee05d272233cb9c21b5075c3 --- src/com/android/calendar/month/SimpleWeekView.kt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/com/android/calendar/month/SimpleWeekView.kt b/src/com/android/calendar/month/SimpleWeekView.kt index d7f25d18..8598e401 100644 --- a/src/com/android/calendar/month/SimpleWeekView.kt +++ b/src/com/android/calendar/month/SimpleWeekView.kt @@ -242,12 +242,12 @@ open class SimpleWeekView(context: Context) : View(context) { open protected fun initView() { p.setFakeBoldText(false) p.setAntiAlias(true) - p.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE as Float) + p.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE.toFloat()) p.setStyle(Style.FILL) mMonthNumPaint = Paint() mMonthNumPaint?.setFakeBoldText(true) mMonthNumPaint?.setAntiAlias(true) - mMonthNumPaint?.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE as Float) + mMonthNumPaint?.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE.toFloat()) mMonthNumPaint?.setColor(mFocusMonthColor) mMonthNumPaint?.setStyle(Style.FILL) mMonthNumPaint?.setTextAlign(Align.CENTER) @@ -327,13 +327,13 @@ open class SimpleWeekView(context: Context) : View(context) { var i = 0 val divisor = 2 * nDays if (mShowWeekNum) { - p.setTextSize(MINI_WK_NUMBER_TEXT_SIZE as Float) + p.setTextSize(MINI_WK_NUMBER_TEXT_SIZE.toFloat()) p.setStyle(Style.FILL) p.setTextAlign(Align.CENTER) p.setAntiAlias(true) p.setColor(mWeekNumColor) val x = (mWidth - mPadding * 2) / divisor + mPadding - canvas.drawText(mDayNumbers[0] ?: "", x as Float, y as Float, p) + canvas.drawText(mDayNumbers[0] ?: "", x.toFloat(), y.toFloat(), p) i++ } var isFocusMonth = mFocusDay[i] @@ -345,13 +345,13 @@ open class SimpleWeekView(context: Context) : View(context) { mMonthNumPaint?.setColor(if (isFocusMonth) mFocusMonthColor else mOtherMonthColor) } if (mHasToday && mToday == i) { - mMonthNumPaint?.setTextSize(MINI_TODAY_NUMBER_TEXT_SIZE as Float) + mMonthNumPaint?.setTextSize(MINI_TODAY_NUMBER_TEXT_SIZE.toFloat()) mMonthNumPaint?.setFakeBoldText(true) } val x = (2 * i + 1) * (mWidth - mPadding * 2) / divisor + mPadding - canvas.drawText(mDayNumbers[i] ?: "", x as Float, y as Float, mMonthNumPaint ?: Paint()) + canvas.drawText(mDayNumbers[i] ?: "", x.toFloat(), y.toFloat(), mMonthNumPaint ?: Paint()) if (mHasToday && mToday == i) { - mMonthNumPaint?.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE as Float) + mMonthNumPaint?.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE.toFloat()) mMonthNumPaint?.setFakeBoldText(false) } i++ @@ -370,16 +370,16 @@ open class SimpleWeekView(context: Context) : View(context) { r.bottom = mHeight - 1 r.left = mSelectedLeft + 1 r.right = mSelectedRight - 1 - p.setStrokeWidth(MINI_TODAY_OUTLINE_WIDTH as Float) + p.setStrokeWidth(MINI_TODAY_OUTLINE_WIDTH.toFloat()) p.setStyle(Style.STROKE) p.setColor(mTodayOutlineColor) canvas.drawRect(r, p) } if (mShowWeekNum) { p.setColor(mDaySeparatorColor) - p.setStrokeWidth(DAY_SEPARATOR_WIDTH as Float) + p.setStrokeWidth(DAY_SEPARATOR_WIDTH.toFloat()) val x = (mWidth - mPadding * 2) / mNumCells + mPadding - canvas.drawLine(x as Float, 0 as Float, x as Float, mHeight as Float, p) + canvas.drawLine(x.toFloat(), 0f, x.toFloat(), mHeight.toFloat(), p) } } -- cgit v1.2.3 From 195aafd714d6dc463ae47b438929fbe1ca173d52 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 25 Jun 2021 16:23:07 +0000 Subject: AOSP/Calendar - Copy of CalendarAppWidgetModel.java The Java code in CalendarAppWidgetModel.java has been copied into a corresponding .kt file. Test: manual - opening both .java and .kt files shows that both are identical. Change-Id: I6c4253e5b949b500805a319f5d06d6fce591a249 --- .../calendar/widget/CalendarAppWidgetModel.kt | 430 +++++++++++++++++++++ 1 file changed, 430 insertions(+) create mode 100644 src/com/android/calendar/widget/CalendarAppWidgetModel.kt diff --git a/src/com/android/calendar/widget/CalendarAppWidgetModel.kt b/src/com/android/calendar/widget/CalendarAppWidgetModel.kt new file mode 100644 index 00000000..a989e18b --- /dev/null +++ b/src/com/android/calendar/widget/CalendarAppWidgetModel.kt @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2010 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.widget; + +import com.android.calendar.R; +import com.android.calendar.Utils; + +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Log; +import android.view.View; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.TimeZone; + +class CalendarAppWidgetModel { + private static final String TAG = CalendarAppWidgetModel.class.getSimpleName(); + private static final boolean LOGD = false; + + private String mHomeTZName; + private boolean mShowTZ; + /** + * {@link RowInfo} is a class that represents a single row in the widget. It + * is actually only a pointer to either a {@link DayInfo} or an + * {@link EventInfo} instance, since a row in the widget might be either a + * day header or an event. + */ + static class RowInfo { + static final int TYPE_DAY = 0; + static final int TYPE_MEETING = 1; + + /** + * mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING) + */ + final int mType; + + /** + * If mType is TYPE_DAY, then mData is the index into day infos. + * Otherwise mType is TYPE_MEETING and mData is the index into event + * infos. + */ + final int mIndex; + + RowInfo(int type, int index) { + mType = type; + mIndex = index; + } + } + + /** + * {@link EventInfo} is a class that represents an event in the widget. It + * contains all of the data necessary to display that event, including the + * properly localized strings and visibility settings. + */ + static class EventInfo { + int visibWhen; // Visibility value for When textview (View.GONE or View.VISIBLE) + String when; + int visibWhere; // Visibility value for Where textview (View.GONE or View.VISIBLE) + String where; + int visibTitle; // Visibility value for Title textview (View.GONE or View.VISIBLE) + String title; + int selfAttendeeStatus; + + long id; + long start; + long end; + boolean allDay; + int color; + + public EventInfo() { + visibWhen = View.GONE; + visibWhere = View.GONE; + visibTitle = View.GONE; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("EventInfo [visibTitle="); + builder.append(visibTitle); + builder.append(", title="); + builder.append(title); + builder.append(", visibWhen="); + builder.append(visibWhen); + builder.append(", id="); + builder.append(id); + builder.append(", when="); + builder.append(when); + builder.append(", visibWhere="); + builder.append(visibWhere); + builder.append(", where="); + builder.append(where); + builder.append(", color="); + builder.append(String.format("0x%x", color)); + builder.append(", selfAttendeeStatus="); + builder.append(selfAttendeeStatus); + builder.append("]"); + return builder.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (allDay ? 1231 : 1237); + result = prime * result + (int) (id ^ (id >>> 32)); + result = prime * result + (int) (end ^ (end >>> 32)); + result = prime * result + (int) (start ^ (start >>> 32)); + result = prime * result + ((title == null) ? 0 : title.hashCode()); + result = prime * result + visibTitle; + result = prime * result + visibWhen; + result = prime * result + visibWhere; + result = prime * result + ((when == null) ? 0 : when.hashCode()); + result = prime * result + ((where == null) ? 0 : where.hashCode()); + result = prime * result + color; + result = prime * result + selfAttendeeStatus; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + EventInfo other = (EventInfo) obj; + if (id != other.id) + return false; + if (allDay != other.allDay) + return false; + if (end != other.end) + return false; + if (start != other.start) + return false; + if (title == null) { + if (other.title != null) + return false; + } else if (!title.equals(other.title)) + return false; + if (visibTitle != other.visibTitle) + return false; + if (visibWhen != other.visibWhen) + return false; + if (visibWhere != other.visibWhere) + return false; + if (when == null) { + if (other.when != null) + return false; + } else if (!when.equals(other.when)) { + return false; + } + if (where == null) { + if (other.where != null) + return false; + } else if (!where.equals(other.where)) { + return false; + } + if (color != other.color) { + return false; + } + if (selfAttendeeStatus != other.selfAttendeeStatus) { + return false; + } + return true; + } + } + + /** + * {@link DayInfo} is a class that represents a day header in the widget. It + * contains all of the data necessary to display that day header, including + * the properly localized string. + */ + static class DayInfo { + + /** The Julian day */ + final int mJulianDay; + + /** The string representation of this day header, to be displayed */ + final String mDayLabel; + + DayInfo(int julianDay, String label) { + mJulianDay = julianDay; + mDayLabel = label; + } + + @Override + public String toString() { + return mDayLabel; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((mDayLabel == null) ? 0 : mDayLabel.hashCode()); + result = prime * result + mJulianDay; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DayInfo other = (DayInfo) obj; + if (mDayLabel == null) { + if (other.mDayLabel != null) + return false; + } else if (!mDayLabel.equals(other.mDayLabel)) + return false; + if (mJulianDay != other.mJulianDay) + return false; + return true; + } + + } + + final List mRowInfos; + final List mEventInfos; + final List mDayInfos; + final Context mContext; + final long mNow; + final int mTodayJulianDay; + final int mMaxJulianDay; + + public CalendarAppWidgetModel(Context context, String timeZone) { + mNow = System.currentTimeMillis(); + Time time = new Time(timeZone); + time.setToNow(); // This is needed for gmtoff to be set + mTodayJulianDay = Time.getJulianDay(mNow, time.gmtoff); + mMaxJulianDay = mTodayJulianDay + CalendarAppWidgetService.MAX_DAYS - 1; + mEventInfos = new ArrayList(50); + mRowInfos = new ArrayList(50); + mDayInfos = new ArrayList(8); + mContext = context; + } + + public void buildFromCursor(Cursor cursor, String timeZone) { + final Time recycle = new Time(timeZone); + final ArrayList> mBuckets = + new ArrayList>(CalendarAppWidgetService.MAX_DAYS); + for (int i = 0; i < CalendarAppWidgetService.MAX_DAYS; i++) { + mBuckets.add(new LinkedList()); + } + recycle.setToNow(); + mShowTZ = !TextUtils.equals(timeZone, Time.getCurrentTimezone()); + if (mShowTZ) { + mHomeTZName = TimeZone.getTimeZone(timeZone).getDisplayName(recycle.isDst != 0, + TimeZone.SHORT); + } + + cursor.moveToPosition(-1); + String tz = Utils.getTimeZone(mContext, null); + while (cursor.moveToNext()) { + final int rowId = cursor.getPosition(); + final long eventId = cursor.getLong(CalendarAppWidgetService.INDEX_EVENT_ID); + final boolean allDay = cursor.getInt(CalendarAppWidgetService.INDEX_ALL_DAY) != 0; + long start = cursor.getLong(CalendarAppWidgetService.INDEX_BEGIN); + long end = cursor.getLong(CalendarAppWidgetService.INDEX_END); + final String title = cursor.getString(CalendarAppWidgetService.INDEX_TITLE); + final String location = + cursor.getString(CalendarAppWidgetService.INDEX_EVENT_LOCATION); + // we don't compute these ourselves because it seems to produce the + // wrong endDay for all day events + final int startDay = cursor.getInt(CalendarAppWidgetService.INDEX_START_DAY); + final int endDay = cursor.getInt(CalendarAppWidgetService.INDEX_END_DAY); + final int color = cursor.getInt(CalendarAppWidgetService.INDEX_COLOR); + final int selfStatus = cursor + .getInt(CalendarAppWidgetService.INDEX_SELF_ATTENDEE_STATUS); + + // Adjust all-day times into local timezone + if (allDay) { + start = Utils.convertAlldayUtcToLocal(recycle, start, tz); + end = Utils.convertAlldayUtcToLocal(recycle, end, tz); + } + + if (LOGD) { + Log.d(TAG, "Row #" + rowId + " allDay:" + allDay + " start:" + start + + " end:" + end + " eventId:" + eventId); + } + + // we might get some extra events when querying, in order to + // deal with all-day events + if (end < mNow) { + continue; + } + + int i = mEventInfos.size(); + mEventInfos.add(populateEventInfo(eventId, allDay, start, end, startDay, endDay, title, + location, color, selfStatus)); + // populate the day buckets that this event falls into + int from = Math.max(startDay, mTodayJulianDay); + int to = Math.min(endDay, mMaxJulianDay); + for (int day = from; day <= to; day++) { + LinkedList bucket = mBuckets.get(day - mTodayJulianDay); + RowInfo rowInfo = new RowInfo(RowInfo.TYPE_MEETING, i); + if (allDay) { + bucket.addFirst(rowInfo); + } else { + bucket.add(rowInfo); + } + } + } + + int day = mTodayJulianDay; + int count = 0; + for (LinkedList bucket : mBuckets) { + if (!bucket.isEmpty()) { + // We don't show day header in today + if (day != mTodayJulianDay) { + final DayInfo dayInfo = populateDayInfo(day, recycle); + // Add the day header + final int dayIndex = mDayInfos.size(); + mDayInfos.add(dayInfo); + mRowInfos.add(new RowInfo(RowInfo.TYPE_DAY, dayIndex)); + } + + // Add the event row infos + mRowInfos.addAll(bucket); + count += bucket.size(); + } + day++; + if (count >= CalendarAppWidgetService.EVENT_MIN_COUNT) { + break; + } + } + } + + private EventInfo populateEventInfo(long eventId, boolean allDay, long start, long end, + int startDay, int endDay, String title, String location, int color, int selfStatus) { + EventInfo eventInfo = new EventInfo(); + + // Compute a human-readable string for the start time of the event + StringBuilder whenString = new StringBuilder(); + int visibWhen; + int flags = DateUtils.FORMAT_ABBREV_ALL; + visibWhen = View.VISIBLE; + if (allDay) { + flags |= DateUtils.FORMAT_SHOW_DATE; + whenString.append(Utils.formatDateRange(mContext, start, end, flags)); + } else { + flags |= DateUtils.FORMAT_SHOW_TIME; + if (DateFormat.is24HourFormat(mContext)) { + flags |= DateUtils.FORMAT_24HOUR; + } + if (endDay > startDay) { + flags |= DateUtils.FORMAT_SHOW_DATE; + } + whenString.append(Utils.formatDateRange(mContext, start, end, flags)); + + if (mShowTZ) { + whenString.append(" ").append(mHomeTZName); + } + } + eventInfo.id = eventId; + eventInfo.start = start; + eventInfo.end = end; + eventInfo.allDay = allDay; + eventInfo.when = whenString.toString(); + eventInfo.visibWhen = visibWhen; + eventInfo.color = color; + eventInfo.selfAttendeeStatus = selfStatus; + + // What + if (TextUtils.isEmpty(title)) { + eventInfo.title = mContext.getString(R.string.no_title_label); + } else { + eventInfo.title = title; + } + eventInfo.visibTitle = View.VISIBLE; + + // Where + if (!TextUtils.isEmpty(location)) { + eventInfo.visibWhere = View.VISIBLE; + eventInfo.where = location; + } else { + eventInfo.visibWhere = View.GONE; + } + return eventInfo; + } + + private DayInfo populateDayInfo(int julianDay, Time recycle) { + long millis = recycle.setJulianDay(julianDay); + int flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE; + + String label; + if (julianDay == mTodayJulianDay + 1) { + label = mContext.getString(R.string.agenda_tomorrow, + Utils.formatDateRange(mContext, millis, millis, flags).toString()); + } else { + flags |= DateUtils.FORMAT_SHOW_WEEKDAY; + label = Utils.formatDateRange(mContext, millis, millis, flags); + } + return new DayInfo(julianDay, label); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("\nCalendarAppWidgetModel [eventInfos="); + builder.append(mEventInfos); + builder.append("]"); + return builder.toString(); + } +} \ No newline at end of file -- cgit v1.2.3 From af3d93fb1ce9ce0aa2d34cf15ea33e43a08dbd30 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 25 Jun 2021 16:42:56 +0000 Subject: AOSP/Calendar - CalendarAppWidgetModel fully converted with bp file Using Android Studio, CalendarAppWidgetModel.java has been fully converted to Kotlin. The only real issue that I ran into dealt with the visibility of some instance fields after the conversion. This was corrected by adding the @JvmField annotation beside each instance field. Another small correction that had to be made was the substitution of ::class for .getClass() since .getClass() is an unresolved function in Kotlin. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.161 OK (22 Tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 6m 24s Total aggregated tests run time: 6m 24s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.98 tests/sec [376 tests / 384855 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 9299 ms || clean = 3246 ms Total preparation time: 9s || Total tear down time: 3s ======================================================= =============== Summary =============== Total Run time: 8m 16s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: I898c10d7662fba1c9ec0d190194d8d135294ce0f --- Android.bp | 1 + .../calendar/widget/CalendarAppWidgetModel.kt | 577 ++++++++++----------- 2 files changed, 279 insertions(+), 299 deletions(-) diff --git a/Android.bp b/Android.bp index 5cec2bc5..3ea5e1fe 100644 --- a/Android.bp +++ b/Android.bp @@ -27,6 +27,7 @@ exclude_srcsd = [ "src/**/calendar/alerts/InitAlarmsService.java", "src/**/calendar/alerts/NotificationMgr.java", "src/**/calendar/alerts/QuickResponseActivity.java", + "src/**/calendar/widget/CalendarAppWidgetModel.java", "src/**/calendar/CalendarController.java", "src/**/calendar/DayOfMonthDrawable.java", "src/**/calendar/Event.java", diff --git a/src/com/android/calendar/widget/CalendarAppWidgetModel.kt b/src/com/android/calendar/widget/CalendarAppWidgetModel.kt index a989e18b..440d178b 100644 --- a/src/com/android/calendar/widget/CalendarAppWidgetModel.kt +++ b/src/com/android/calendar/widget/CalendarAppWidgetModel.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,418 +13,397 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar.widget + +import com.android.calendar.R +import com.android.calendar.Utils +import android.content.Context +import android.database.Cursor +import android.text.TextUtils +import android.text.format.DateFormat +import android.text.format.DateUtils +import android.text.format.Time +import android.util.Log +import android.view.View +import java.util.ArrayList +import java.util.LinkedList +import java.util.TimeZone + +internal class CalendarAppWidgetModel(context: Context, timeZone: String?) { + private var mHomeTZName: String? = null + private var mShowTZ = false -package com.android.calendar.widget; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.content.Context; -import android.database.Cursor; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.View; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.TimeZone; - -class CalendarAppWidgetModel { - private static final String TAG = CalendarAppWidgetModel.class.getSimpleName(); - private static final boolean LOGD = false; - - private String mHomeTZName; - private boolean mShowTZ; /** - * {@link RowInfo} is a class that represents a single row in the widget. It - * is actually only a pointer to either a {@link DayInfo} or an - * {@link EventInfo} instance, since a row in the widget might be either a + * [RowInfo] is a class that represents a single row in the widget. It + * is actually only a pointer to either a [DayInfo] or an + * [EventInfo] instance, since a row in the widget might be either a * day header or an event. */ - static class RowInfo { - static final int TYPE_DAY = 0; - static final int TYPE_MEETING = 1; - + internal class RowInfo( /** - * mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING) + * mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING) */ - final int mType; - + @JvmField val mType: Int, /** * If mType is TYPE_DAY, then mData is the index into day infos. * Otherwise mType is TYPE_MEETING and mData is the index into event * infos. */ - final int mIndex; - - RowInfo(int type, int index) { - mType = type; - mIndex = index; + @JvmField val mIndex: Int + ) { + companion object { + const val TYPE_DAY = 0 + const val TYPE_MEETING = 1 } } /** - * {@link EventInfo} is a class that represents an event in the widget. It + * [EventInfo] is a class that represents an event in the widget. It * contains all of the data necessary to display that event, including the * properly localized strings and visibility settings. */ - static class EventInfo { - int visibWhen; // Visibility value for When textview (View.GONE or View.VISIBLE) - String when; - int visibWhere; // Visibility value for Where textview (View.GONE or View.VISIBLE) - String where; - int visibTitle; // Visibility value for Title textview (View.GONE or View.VISIBLE) - String title; - int selfAttendeeStatus; - - long id; - long start; - long end; - boolean allDay; - int color; - - public EventInfo() { - visibWhen = View.GONE; - visibWhere = View.GONE; - visibTitle = View.GONE; - } + internal class EventInfo { + // Visibility value for When textview (View.GONE or View.VISIBLE) + @JvmField var visibWhen: Int + @JvmField var `when`: String? = null + // Visibility value for Where textview (View.GONE or View.VISIBLE) + @JvmField var visibWhere: Int + @JvmField var where: String? = null + // Visibility value for Title textview (View.GONE or View.VISIBLE) + @JvmField var visibTitle: Int + @JvmField var title: String? = null + @JvmField var selfAttendeeStatus = 0 + @JvmField var id: Long = 0 + @JvmField var start: Long = 0 + @JvmField var end: Long = 0 + @JvmField var allDay = false + @JvmField var color = 0 @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("EventInfo [visibTitle="); - builder.append(visibTitle); - builder.append(", title="); - builder.append(title); - builder.append(", visibWhen="); - builder.append(visibWhen); - builder.append(", id="); - builder.append(id); - builder.append(", when="); - builder.append(when); - builder.append(", visibWhere="); - builder.append(visibWhere); - builder.append(", where="); - builder.append(where); - builder.append(", color="); - builder.append(String.format("0x%x", color)); - builder.append(", selfAttendeeStatus="); - builder.append(selfAttendeeStatus); - builder.append("]"); - return builder.toString(); + override fun toString(): String { + val builder = StringBuilder() + builder.append("EventInfo [visibTitle=") + builder.append(visibTitle) + builder.append(", title=") + builder.append(title) + builder.append(", visibWhen=") + builder.append(visibWhen) + builder.append(", id=") + builder.append(id) + builder.append(", when=") + builder.append(`when`) + builder.append(", visibWhere=") + builder.append(visibWhere) + builder.append(", where=") + builder.append(where) + builder.append(", color=") + builder.append(String.format("0x%x", color)) + builder.append(", selfAttendeeStatus=") + builder.append(selfAttendeeStatus) + builder.append("]") + return builder.toString() } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (allDay ? 1231 : 1237); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (int) (end ^ (end >>> 32)); - result = prime * result + (int) (start ^ (start >>> 32)); - result = prime * result + ((title == null) ? 0 : title.hashCode()); - result = prime * result + visibTitle; - result = prime * result + visibWhen; - result = prime * result + visibWhere; - result = prime * result + ((when == null) ? 0 : when.hashCode()); - result = prime * result + ((where == null) ? 0 : where.hashCode()); - result = prime * result + color; - result = prime * result + selfAttendeeStatus; - return result; + override fun hashCode(): Int { + val prime = 31 + var result = 1 + result = prime * result + if (allDay) 1231 else 1237 + result = prime * result + (id xor (id ushr 32)).toInt() + result = prime * result + (end xor (end ushr 32)).toInt() + result = prime * result + (start xor (start ushr 32)).toInt() + result = prime * result + if (title == null) 0 else title!!.hashCode() + result = prime * result + visibTitle + result = prime * result + visibWhen + result = prime * result + visibWhere + result = prime * result + if (`when` == null) 0 else `when`!!.hashCode() + result = prime * result + if (where == null) 0 else where!!.hashCode() + result = prime * result + color + result = prime * result + selfAttendeeStatus + return result } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - EventInfo other = (EventInfo) obj; - if (id != other.id) - return false; - if (allDay != other.allDay) - return false; - if (end != other.end) - return false; - if (start != other.start) - return false; + override fun equals(obj: Any?): Boolean { + if (this == obj) return true + if (obj == null) return false + if (this::class != obj::class) return false + val other = obj as EventInfo + if (id != other.id) return false + if (allDay != other.allDay) return false + if (end != other.end) return false + if (start != other.start) return false if (title == null) { - if (other.title != null) - return false; - } else if (!title.equals(other.title)) - return false; - if (visibTitle != other.visibTitle) - return false; - if (visibWhen != other.visibWhen) - return false; - if (visibWhere != other.visibWhere) - return false; - if (when == null) { - if (other.when != null) - return false; - } else if (!when.equals(other.when)) { - return false; + if (other.title != null) return false + } else if (!title!!.equals(other.title)) return false + if (visibTitle != other.visibTitle) return false + if (visibWhen != other.visibWhen) return false + if (visibWhere != other.visibWhere) return false + if (`when` == null) { + if (other.`when` != null) return false + } else if (!`when`!!.equals(other.`when`)) { + return false } if (where == null) { - if (other.where != null) - return false; - } else if (!where.equals(other.where)) { - return false; + if (other.where != null) return false + } else if (!where!!.equals(other.where)) { + return false } if (color != other.color) { - return false; + return false } - if (selfAttendeeStatus != other.selfAttendeeStatus) { - return false; - } - return true; + return if (selfAttendeeStatus != other.selfAttendeeStatus) { + false + } else true + } + + init { + visibWhen = View.GONE + visibWhere = View.GONE + visibTitle = View.GONE } } /** - * {@link DayInfo} is a class that represents a day header in the widget. It + * [DayInfo] is a class that represents a day header in the widget. It * contains all of the data necessary to display that day header, including * the properly localized string. */ - static class DayInfo { - - /** The Julian day */ - final int mJulianDay; - - /** The string representation of this day header, to be displayed */ - final String mDayLabel; - - DayInfo(int julianDay, String label) { - mJulianDay = julianDay; - mDayLabel = label; - } - + internal class DayInfo( + /** The Julian day */ + @JvmField var mJulianDay: Int, + /** The string representation of this day header, to be displayed */ + @JvmField var mDayLabel: String? = null + ) { @Override - public String toString() { - return mDayLabel; + override fun toString(): String { + return mDayLabel as String } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mDayLabel == null) ? 0 : mDayLabel.hashCode()); - result = prime * result + mJulianDay; - return result; + override fun hashCode(): Int { + val prime = 31 + var result = 1 + result = prime * result + (mDayLabel?.hashCode() ?: 0) + result = prime * result + mJulianDay + return result } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DayInfo other = (DayInfo) obj; + override fun equals(obj: Any?): Boolean { + if (this == obj) return true + if (obj == null) return false + if (this::class !== obj::class) return false + val other = obj as DayInfo if (mDayLabel == null) { - if (other.mDayLabel != null) - return false; - } else if (!mDayLabel.equals(other.mDayLabel)) - return false; - if (mJulianDay != other.mJulianDay) - return false; - return true; + if (other.mDayLabel != null) return false + } else if (!mDayLabel.equals(other.mDayLabel)) return false + return if (mJulianDay != other.mJulianDay) false else true } - - } - - final List mRowInfos; - final List mEventInfos; - final List mDayInfos; - final Context mContext; - final long mNow; - final int mTodayJulianDay; - final int mMaxJulianDay; - - public CalendarAppWidgetModel(Context context, String timeZone) { - mNow = System.currentTimeMillis(); - Time time = new Time(timeZone); - time.setToNow(); // This is needed for gmtoff to be set - mTodayJulianDay = Time.getJulianDay(mNow, time.gmtoff); - mMaxJulianDay = mTodayJulianDay + CalendarAppWidgetService.MAX_DAYS - 1; - mEventInfos = new ArrayList(50); - mRowInfos = new ArrayList(50); - mDayInfos = new ArrayList(8); - mContext = context; } - public void buildFromCursor(Cursor cursor, String timeZone) { - final Time recycle = new Time(timeZone); - final ArrayList> mBuckets = - new ArrayList>(CalendarAppWidgetService.MAX_DAYS); - for (int i = 0; i < CalendarAppWidgetService.MAX_DAYS; i++) { - mBuckets.add(new LinkedList()); + @JvmField val mRowInfos: ArrayList + @JvmField val mEventInfos: ArrayList + @JvmField val mDayInfos: ArrayList + @JvmField val mContext: Context? + @JvmField val mNow: Long + @JvmField val mTodayJulianDay: Int + @JvmField val mMaxJulianDay: Int + fun buildFromCursor(cursor: Cursor, timeZone: String?) { + val recycle = Time(timeZone) + val mBuckets: ArrayList> = + ArrayList>(CalendarAppWidgetService.MAX_DAYS) + for (i in 0 until CalendarAppWidgetService.MAX_DAYS) { + mBuckets.add(LinkedList()) } - recycle.setToNow(); - mShowTZ = !TextUtils.equals(timeZone, Time.getCurrentTimezone()); + recycle.setToNow() + mShowTZ = !TextUtils.equals(timeZone, Time.getCurrentTimezone()) if (mShowTZ) { - mHomeTZName = TimeZone.getTimeZone(timeZone).getDisplayName(recycle.isDst != 0, - TimeZone.SHORT); + mHomeTZName = TimeZone.getTimeZone(timeZone).getDisplayName( + recycle.isDst !== 0, + TimeZone.SHORT + ) } - - cursor.moveToPosition(-1); - String tz = Utils.getTimeZone(mContext, null); + cursor.moveToPosition(-1) + val tz = Utils.getTimeZone(mContext, null) while (cursor.moveToNext()) { - final int rowId = cursor.getPosition(); - final long eventId = cursor.getLong(CalendarAppWidgetService.INDEX_EVENT_ID); - final boolean allDay = cursor.getInt(CalendarAppWidgetService.INDEX_ALL_DAY) != 0; - long start = cursor.getLong(CalendarAppWidgetService.INDEX_BEGIN); - long end = cursor.getLong(CalendarAppWidgetService.INDEX_END); - final String title = cursor.getString(CalendarAppWidgetService.INDEX_TITLE); - final String location = - cursor.getString(CalendarAppWidgetService.INDEX_EVENT_LOCATION); + val rowId: Int = cursor.getPosition() + val eventId: Long = cursor.getLong(CalendarAppWidgetService.INDEX_EVENT_ID) + val allDay = cursor.getInt(CalendarAppWidgetService.INDEX_ALL_DAY) !== 0 + var start: Long = cursor.getLong(CalendarAppWidgetService.INDEX_BEGIN) + var end: Long = cursor.getLong(CalendarAppWidgetService.INDEX_END) + val title: String = cursor.getString(CalendarAppWidgetService.INDEX_TITLE) + val location: String = cursor.getString(CalendarAppWidgetService.INDEX_EVENT_LOCATION) // we don't compute these ourselves because it seems to produce the // wrong endDay for all day events - final int startDay = cursor.getInt(CalendarAppWidgetService.INDEX_START_DAY); - final int endDay = cursor.getInt(CalendarAppWidgetService.INDEX_END_DAY); - final int color = cursor.getInt(CalendarAppWidgetService.INDEX_COLOR); - final int selfStatus = cursor - .getInt(CalendarAppWidgetService.INDEX_SELF_ATTENDEE_STATUS); + val startDay: Int = cursor.getInt(CalendarAppWidgetService.INDEX_START_DAY) + val endDay: Int = cursor.getInt(CalendarAppWidgetService.INDEX_END_DAY) + val color: Int = cursor.getInt(CalendarAppWidgetService.INDEX_COLOR) + val selfStatus: Int = cursor + .getInt(CalendarAppWidgetService.INDEX_SELF_ATTENDEE_STATUS) // Adjust all-day times into local timezone if (allDay) { - start = Utils.convertAlldayUtcToLocal(recycle, start, tz); - end = Utils.convertAlldayUtcToLocal(recycle, end, tz); + start = Utils.convertAlldayUtcToLocal(recycle, start, tz as String) + end = Utils.convertAlldayUtcToLocal(recycle, end, tz as String) } - if (LOGD) { - Log.d(TAG, "Row #" + rowId + " allDay:" + allDay + " start:" + start - + " end:" + end + " eventId:" + eventId); + Log.d( + TAG, "Row #" + rowId + " allDay:" + allDay + " start:" + start + + " end:" + end + " eventId:" + eventId + ) } // we might get some extra events when querying, in order to // deal with all-day events if (end < mNow) { - continue; + continue } - - int i = mEventInfos.size(); - mEventInfos.add(populateEventInfo(eventId, allDay, start, end, startDay, endDay, title, - location, color, selfStatus)); + val i: Int = mEventInfos.size + mEventInfos.add( + populateEventInfo( + eventId, allDay, start, end, startDay, endDay, title, + location, color, selfStatus + ) + ) // populate the day buckets that this event falls into - int from = Math.max(startDay, mTodayJulianDay); - int to = Math.min(endDay, mMaxJulianDay); - for (int day = from; day <= to; day++) { - LinkedList bucket = mBuckets.get(day - mTodayJulianDay); - RowInfo rowInfo = new RowInfo(RowInfo.TYPE_MEETING, i); + val from: Int = Math.max(startDay, mTodayJulianDay) + val to: Int = Math.min(endDay, mMaxJulianDay) + for (day in from..to) { + val bucket: LinkedList = mBuckets.get(day - mTodayJulianDay) + val rowInfo = RowInfo(RowInfo.TYPE_MEETING, i) if (allDay) { - bucket.addFirst(rowInfo); + bucket.addFirst(rowInfo) } else { - bucket.add(rowInfo); + bucket.add(rowInfo) } } } - - int day = mTodayJulianDay; - int count = 0; - for (LinkedList bucket : mBuckets) { + var day = mTodayJulianDay + var count = 0 + for (bucket in mBuckets) { if (!bucket.isEmpty()) { // We don't show day header in today if (day != mTodayJulianDay) { - final DayInfo dayInfo = populateDayInfo(day, recycle); + val dayInfo = populateDayInfo(day, recycle) // Add the day header - final int dayIndex = mDayInfos.size(); - mDayInfos.add(dayInfo); - mRowInfos.add(new RowInfo(RowInfo.TYPE_DAY, dayIndex)); + val dayIndex: Int = mDayInfos.size + mDayInfos.add(dayInfo as CalendarAppWidgetModel.DayInfo) + mRowInfos.add(RowInfo(RowInfo.TYPE_DAY, dayIndex)) } // Add the event row infos - mRowInfos.addAll(bucket); - count += bucket.size(); + mRowInfos.addAll(bucket) + count += bucket.size } - day++; + day++ if (count >= CalendarAppWidgetService.EVENT_MIN_COUNT) { - break; + break } } } - private EventInfo populateEventInfo(long eventId, boolean allDay, long start, long end, - int startDay, int endDay, String title, String location, int color, int selfStatus) { - EventInfo eventInfo = new EventInfo(); + private fun populateEventInfo( + eventId: Long, + allDay: Boolean, + start: Long, + end: Long, + startDay: Int, + endDay: Int, + title: String, + location: String, + color: Int, + selfStatus: Int + ): EventInfo { + val eventInfo = EventInfo() // Compute a human-readable string for the start time of the event - StringBuilder whenString = new StringBuilder(); - int visibWhen; - int flags = DateUtils.FORMAT_ABBREV_ALL; - visibWhen = View.VISIBLE; + val whenString = StringBuilder() + val visibWhen: Int + var flags: Int = DateUtils.FORMAT_ABBREV_ALL + visibWhen = View.VISIBLE if (allDay) { - flags |= DateUtils.FORMAT_SHOW_DATE; - whenString.append(Utils.formatDateRange(mContext, start, end, flags)); + flags = flags or DateUtils.FORMAT_SHOW_DATE + whenString.append(Utils.formatDateRange(mContext, start, end, flags)) } else { - flags |= DateUtils.FORMAT_SHOW_TIME; + flags = flags or DateUtils.FORMAT_SHOW_TIME if (DateFormat.is24HourFormat(mContext)) { - flags |= DateUtils.FORMAT_24HOUR; + flags = flags or DateUtils.FORMAT_24HOUR } if (endDay > startDay) { - flags |= DateUtils.FORMAT_SHOW_DATE; + flags = flags or DateUtils.FORMAT_SHOW_DATE } - whenString.append(Utils.formatDateRange(mContext, start, end, flags)); - + whenString.append(Utils.formatDateRange(mContext, start, end, flags)) if (mShowTZ) { - whenString.append(" ").append(mHomeTZName); + whenString.append(" ").append(mHomeTZName) } } - eventInfo.id = eventId; - eventInfo.start = start; - eventInfo.end = end; - eventInfo.allDay = allDay; - eventInfo.when = whenString.toString(); - eventInfo.visibWhen = visibWhen; - eventInfo.color = color; - eventInfo.selfAttendeeStatus = selfStatus; + eventInfo.id = eventId + eventInfo.start = start + eventInfo.end = end + eventInfo.allDay = allDay + eventInfo.`when` = whenString.toString() + eventInfo.visibWhen = visibWhen + eventInfo.color = color + eventInfo.selfAttendeeStatus = selfStatus // What if (TextUtils.isEmpty(title)) { - eventInfo.title = mContext.getString(R.string.no_title_label); + eventInfo.title = mContext?.getString(R.string.no_title_label) } else { - eventInfo.title = title; + eventInfo.title = title } - eventInfo.visibTitle = View.VISIBLE; + eventInfo.visibTitle = View.VISIBLE // Where if (!TextUtils.isEmpty(location)) { - eventInfo.visibWhere = View.VISIBLE; - eventInfo.where = location; + eventInfo.visibWhere = View.VISIBLE + eventInfo.where = location } else { - eventInfo.visibWhere = View.GONE; + eventInfo.visibWhere = View.GONE } - return eventInfo; + return eventInfo } - private DayInfo populateDayInfo(int julianDay, Time recycle) { - long millis = recycle.setJulianDay(julianDay); - int flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE; - - String label; + private fun populateDayInfo(julianDay: Int, recycle: Time?): DayInfo? { + val millis: Long = recycle?.setJulianDay(julianDay) as Long + var flags: Int = DateUtils.FORMAT_ABBREV_ALL or DateUtils.FORMAT_SHOW_DATE + val label: String? if (julianDay == mTodayJulianDay + 1) { - label = mContext.getString(R.string.agenda_tomorrow, - Utils.formatDateRange(mContext, millis, millis, flags).toString()); + label = mContext?.getString( + R.string.agenda_tomorrow, + Utils.formatDateRange(mContext, millis, millis, flags).toString() + ) } else { - flags |= DateUtils.FORMAT_SHOW_WEEKDAY; - label = Utils.formatDateRange(mContext, millis, millis, flags); + flags = flags or DateUtils.FORMAT_SHOW_WEEKDAY + label = Utils.formatDateRange(mContext, millis, millis, flags) } - return new DayInfo(julianDay, label); + return DayInfo(julianDay, label as String) } @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("\nCalendarAppWidgetModel [eventInfos="); - builder.append(mEventInfos); - builder.append("]"); - return builder.toString(); + override fun toString(): String { + val builder = StringBuilder() + builder.append("\nCalendarAppWidgetModel [eventInfos=") + builder.append(mEventInfos) + builder.append("]") + return builder.toString() + } + + companion object { + private val TAG: String = CalendarAppWidgetModel::class.java.getSimpleName() + private const val LOGD = false + } + + init { + mNow = System.currentTimeMillis() + val time = Time(timeZone) + time.setToNow() // This is needed for gmtoff to be set + mTodayJulianDay = Time.getJulianDay(mNow, time.gmtoff) + mMaxJulianDay = mTodayJulianDay + CalendarAppWidgetService.MAX_DAYS - 1 + mEventInfos = ArrayList(50) + mRowInfos = ArrayList(50) + mDayInfos = ArrayList(8) + mContext = context } } \ No newline at end of file -- cgit v1.2.3 From 8f2ef620bee0728be75d441e5f51bbf8ab1dc54c Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Sat, 26 Jun 2021 01:31:01 +0000 Subject: AOSP/Calendar - Month View Bug Fix Prior to this change, clicking on a certain day in the month viewer would bring the user to a random day in November in 1956. This change resolves this issue. I believe that the elimination of the firstJulianDay variable has fixed this issue. Test: manual - open the AOSP Calendar app --> go into the month view --> click on a particular day --> ensure that the day viewer you've now entered has the accurate day, month, and year. Change-Id: Ib3b8a93be8e7b44d2f158b48ddf36aa2283672ca --- src/com/android/calendar/month/SimpleWeekView.kt | 151 +++++++++++++---------- 1 file changed, 89 insertions(+), 62 deletions(-) diff --git a/src/com/android/calendar/month/SimpleWeekView.kt b/src/com/android/calendar/month/SimpleWeekView.kt index 8598e401..7c1ef44e 100644 --- a/src/com/android/calendar/month/SimpleWeekView.kt +++ b/src/com/android/calendar/month/SimpleWeekView.kt @@ -52,17 +52,23 @@ open class SimpleWeekView(context: Context) : View(context) { @JvmField protected var mMonthNumPaint: Paint? = null @JvmField protected var mSelectedDayLine: Drawable + // Cache the number strings so we don't have to recompute them each time + @JvmField protected var mDayNumbers: Array? = null + + // Quick lookup for checking which days are in the focus month + @JvmField protected var mFocusDay: BooleanArray? = null + + // Quick lookup for checking which days are in an odd month (to set a different background) + @JvmField protected var mOddMonth: BooleanArray? = null + // The Julian day of the first day displayed by this item - var firstJulianDay = -1 - protected set + @JvmField protected var mFirstJulianDay = -1 // The month of the first day in this week - var firstMonth = -1 - protected set + @JvmField protected var firstMonth = -1 // The month of the last day in this week - var lastMonth = -1 - protected set + @JvmField protected var lastMonth = -1 // The position of this week, equivalent to weeks since the week of Jan 1st, // 1970 @@ -104,9 +110,6 @@ open class SimpleWeekView(context: Context) : View(context) { // The right edge of the selected day @JvmField protected var mSelectedRight = -1 - // The Julian day of the first day displayed by this item - @JvmField protected var mFirstJulianDay = -1 - // The timezone to display times/dates in (used for determining when Today // is) @JvmField protected var mTimeZone: String = Time.getCurrentTimezone() @@ -118,15 +121,6 @@ open class SimpleWeekView(context: Context) : View(context) { @JvmField protected var mTodayOutlineColor: Int @JvmField protected var mWeekNumColor: Int - // Cache the number strings so we don't have to recompute them each time - @JvmField protected var mDayNumbers: Array = arrayOfNulls(mNumCells) - - // Quick lookup for checking which days are in the focus month - @JvmField protected var mFocusDay: BooleanArray = BooleanArray(mNumCells) - - // Quick lookup for checking which days are in an odd month (to set a different background) - @JvmField protected var mOddMonth: BooleanArray = BooleanArray(mNumCells) - /** * Sets all the parameters for displaying this week. The only required * parameter is the week number. Other parameters have a default value and @@ -138,7 +132,7 @@ open class SimpleWeekView(context: Context) : View(context) { * [.VIEW_PARAMS_HEIGHT] * @param tz The time zone this view should reference times in */ - open fun setWeekParams(params: HashMap, tz: String) { + open fun setWeekParams(params: HashMap, tz: String) { if (!params.containsKey(VIEW_PARAMS_WEEK)) { throw InvalidParameterException("You must specify the week number for this view") } @@ -146,24 +140,25 @@ open class SimpleWeekView(context: Context) : View(context) { mTimeZone = tz // We keep the current value for any params not present if (params.containsKey(VIEW_PARAMS_HEIGHT)) { - mHeight = (params.get(VIEW_PARAMS_HEIGHT) ?: 0) as Int + mHeight = params.get(VIEW_PARAMS_HEIGHT) as Int if (mHeight < MIN_HEIGHT) { mHeight = MIN_HEIGHT } } if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) { - mSelectedDay = (params.get(VIEW_PARAMS_SELECTED_DAY) ?: 0) as Int + mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY) as Int } mHasSelectedDay = mSelectedDay != -1 if (params.containsKey(VIEW_PARAMS_NUM_DAYS)) { - mNumDays = (params.get(VIEW_PARAMS_NUM_DAYS) ?: 0) as Int + mNumDays = params.get(VIEW_PARAMS_NUM_DAYS) as Int } if (params.containsKey(VIEW_PARAMS_SHOW_WK_NUM)) { - mShowWeekNum = if ((params.get(VIEW_PARAMS_SHOW_WK_NUM) ?: 0) as Int != 0) { - true - } else { - false - } + mShowWeekNum = + if (params.get(VIEW_PARAMS_SHOW_WK_NUM) != 0) { + true + } else { + false + } } mNumCells = if (mShowWeekNum) mNumDays + 1 else mNumDays @@ -171,7 +166,7 @@ open class SimpleWeekView(context: Context) : View(context) { mDayNumbers = arrayOfNulls(mNumCells) mFocusDay = BooleanArray(mNumCells) mOddMonth = BooleanArray(mNumCells) - mWeek = (params.get(VIEW_PARAMS_WEEK) ?: 0) as Int + mWeek = params.get(VIEW_PARAMS_WEEK) as Int val julianMonday: Int = Utils.getJulianMondayFromWeeksSinceEpoch(mWeek) val time = Time(tz) time.setJulianDay(julianMonday) @@ -179,11 +174,11 @@ open class SimpleWeekView(context: Context) : View(context) { // If we're showing the week number calculate it based on Monday var i = 0 if (mShowWeekNum) { - mDayNumbers[0] = Integer.toString(time.getWeekNumber()) + mDayNumbers!![0] = Integer.toString(time.getWeekNumber()) i++ } if (params.containsKey(VIEW_PARAMS_WEEK_START)) { - mWeekStart = (params.get(VIEW_PARAMS_WEEK_START) ?: 0) as Int + mWeekStart = params.get(VIEW_PARAMS_WEEK_START) as Int } // Now adjust our starting day based on the start day of the week @@ -197,7 +192,7 @@ open class SimpleWeekView(context: Context) : View(context) { time.monthDay -= diff time.normalize(true) } - firstJulianDay = Time.getJulianDay(time.toMillis(true), time.gmtoff) + mFirstJulianDay = Time.getJulianDay(time.toMillis(true), time.gmtoff) firstMonth = time.month // Figure out what day today is @@ -206,22 +201,23 @@ open class SimpleWeekView(context: Context) : View(context) { mHasToday = false mToday = -1 val focusMonth = if (params.containsKey(VIEW_PARAMS_FOCUS_MONTH)) params.get( - VIEW_PARAMS_FOCUS_MONTH) else DEFAULT_FOCUS_MONTH + VIEW_PARAMS_FOCUS_MONTH + ) else DEFAULT_FOCUS_MONTH while (i < mNumCells) { if (time.monthDay === 1) { firstMonth = time.month } - mOddMonth[i] = time.month % 2 === 1 + mOddMonth!![i] = time.month % 2 === 1 if (time.month === focusMonth) { - mFocusDay[i] = true + mFocusDay!![i] = true } else { - mFocusDay[i] = false + mFocusDay!![i] = false } if (time.year === today.year && time.yearDay === today.yearDay) { mHasToday = true mToday = i } - mDayNumbers[i] = Integer.toString(time.monthDay++) + mDayNumbers!![i] = Integer.toString(time.monthDay++) time.normalize(true) i++ } @@ -239,7 +235,7 @@ open class SimpleWeekView(context: Context) : View(context) { * Sets up the text and style properties for painting. Override this if you * want to use a different paint. */ - open protected fun initView() { + protected open fun initView() { p.setFakeBoldText(false) p.setAntiAlias(true) p.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE.toFloat()) @@ -253,6 +249,33 @@ open class SimpleWeekView(context: Context) : View(context) { mMonthNumPaint?.setTextAlign(Align.CENTER) } + /** + * Returns the month of the first day in this week + * + * @return The month the first day of this view is in + */ + fun getFirstMonth(): Int { + return firstMonth + } + + /** + * Returns the month of the last day in this week + * + * @return The month the last day of this view is in + */ + fun getLastMonth(): Int { + return lastMonth + } + + /** + * Returns the julian day of the first day in this view. + * + * @return The julian day of the first day in the view. + */ + fun getFirstJulianDay(): Int { + return mFirstJulianDay + } + /** * Calculates the day that the given x position is in, accounting for week * number. Returns a Time referencing that day or null if @@ -262,14 +285,14 @@ open class SimpleWeekView(context: Context) : View(context) { * in a day */ open fun getDayFromLocation(x: Float): Time? { - val dayStart = if (mShowWeekNum) (mWidth - mPadding * 2) / mNumCells + mPadding - else mPadding + val dayStart = + if (mShowWeekNum) (mWidth - mPadding * 2) / mNumCells + mPadding else mPadding if (x < dayStart || x > mWidth - mPadding) { return null } // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels val dayPosition = ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)).toInt() - var day = firstJulianDay + dayPosition + var day = mFirstJulianDay + dayPosition val time = Time(mTimeZone) if (mWeek == 0) { // This week is weird... @@ -286,7 +309,7 @@ open class SimpleWeekView(context: Context) : View(context) { } @Override - override protected fun onDraw(canvas: Canvas) { + protected override fun onDraw(canvas: Canvas) { drawBackground(canvas) drawWeekNums(canvas) drawDaySeparators(canvas) @@ -298,7 +321,7 @@ open class SimpleWeekView(context: Context) : View(context) { * * @param canvas The canvas to draw on */ - open protected fun drawBackground(canvas: Canvas) { + protected open fun drawBackground(canvas: Canvas) { if (mHasSelectedDay) { p.setColor(mSelectedWeekBGColor) p.setStyle(Style.FILL) @@ -321,7 +344,7 @@ open class SimpleWeekView(context: Context) : View(context) { * * @param canvas The canvas to draw on */ - open protected fun drawWeekNums(canvas: Canvas) { + protected open fun drawWeekNums(canvas: Canvas) { val y = (mHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2 - DAY_SEPARATOR_WIDTH val nDays = mNumCells var i = 0 @@ -333,15 +356,15 @@ open class SimpleWeekView(context: Context) : View(context) { p.setAntiAlias(true) p.setColor(mWeekNumColor) val x = (mWidth - mPadding * 2) / divisor + mPadding - canvas.drawText(mDayNumbers[0] ?: "", x.toFloat(), y.toFloat(), p) + canvas.drawText(mDayNumbers!![0] as String, x.toFloat(), y.toFloat(), p) i++ } - var isFocusMonth = mFocusDay[i] + var isFocusMonth = mFocusDay!![i] mMonthNumPaint?.setColor(if (isFocusMonth) mFocusMonthColor else mOtherMonthColor) mMonthNumPaint?.setFakeBoldText(false) while (i < nDays) { - if (mFocusDay[i] != isFocusMonth) { - isFocusMonth = mFocusDay[i] + if (mFocusDay!![i] != isFocusMonth) { + isFocusMonth = mFocusDay!![i] mMonthNumPaint?.setColor(if (isFocusMonth) mFocusMonthColor else mOtherMonthColor) } if (mHasToday && mToday == i) { @@ -349,7 +372,8 @@ open class SimpleWeekView(context: Context) : View(context) { mMonthNumPaint?.setFakeBoldText(true) } val x = (2 * i + 1) * (mWidth - mPadding * 2) / divisor + mPadding - canvas.drawText(mDayNumbers[i] ?: "", x.toFloat(), y.toFloat(), mMonthNumPaint ?: Paint()) + canvas.drawText(mDayNumbers!![i] as String, x.toFloat(), y.toFloat(), + mMonthNumPaint as Paint) if (mHasToday && mToday == i) { mMonthNumPaint?.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE.toFloat()) mMonthNumPaint?.setFakeBoldText(false) @@ -364,7 +388,7 @@ open class SimpleWeekView(context: Context) : View(context) { * * @param canvas The canvas to draw on */ - open protected fun drawDaySeparators(canvas: Canvas) { + protected open fun drawDaySeparators(canvas: Canvas) { if (mHasSelectedDay) { r.top = 1 r.bottom = mHeight - 1 @@ -384,7 +408,7 @@ open class SimpleWeekView(context: Context) : View(context) { } @Override - override protected fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + protected override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { mWidth = w updateSelectionPositions() } @@ -392,7 +416,7 @@ open class SimpleWeekView(context: Context) : View(context) { /** * This calculates the positions for the selected day lines. */ - open protected fun updateSelectionPositions() { + protected open fun updateSelectionPositions() { if (mHasSelectedDay) { var selectedPosition = mSelectedDay - mWeekStart if (selectedPosition < 0) { @@ -401,15 +425,15 @@ open class SimpleWeekView(context: Context) : View(context) { if (mShowWeekNum) { selectedPosition++ } - mSelectedLeft = (selectedPosition * (mWidth - mPadding * 2) / mNumCells - + mPadding) - mSelectedRight = ((selectedPosition + 1) * (mWidth - mPadding * 2) / mNumCells - + mPadding) + mSelectedLeft = (selectedPosition * (mWidth - mPadding * 2) / mNumCells + + mPadding) + mSelectedRight = ((selectedPosition + 1) * (mWidth - mPadding * 2) / mNumCells + + mPadding) } } @Override - override protected fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + protected override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight) } @@ -419,19 +443,22 @@ open class SimpleWeekView(context: Context) : View(context) { // only send accessibility events if accessibility and exploration are // on. val am: AccessibilityManager = context - .getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager + .getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager if (!am.isEnabled() || !am.isTouchExplorationEnabled()) { return super.onHoverEvent(event) } if (event.getAction() !== MotionEvent.ACTION_HOVER_EXIT) { val hover: Time? = getDayFromLocation(event.getX()) - if (hover != null - && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) !== 0)) { + if (hover != null && + (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) !== 0) + ) { val millis: Long = hover.toMillis(true) - val date = Utils.formatDateRange(context, millis, millis, - DateUtils.FORMAT_SHOW_DATE) + val date: String? = Utils.formatDateRange( + context, millis, millis, + DateUtils.FORMAT_SHOW_DATE + ) val accessEvent: AccessibilityEvent = - AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) accessEvent.getText().add(date) sendAccessibilityEventUnchecked(accessEvent) mLastHoverTime = hover -- cgit v1.2.3 From 817a2d3382991f62e6f10af6694c8727e347f65b Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Mon, 28 Jun 2021 21:14:08 +0000 Subject: AOSP/Calendar - Copy of MonthByWeekFragment.java The Java code in MonthByWeekFragment.java has been copied into a corresponding .kt file. Test: manual - opening both files shows that they are identical. Change-Id: I6d2ab8bfa5961f759a06022e73bdd23aee1d036f --- .../android/calendar/month/MonthByWeekFragment.kt | 494 +++++++++++++++++++++ 1 file changed, 494 insertions(+) create mode 100644 src/com/android/calendar/month/MonthByWeekFragment.kt diff --git a/src/com/android/calendar/month/MonthByWeekFragment.kt b/src/com/android/calendar/month/MonthByWeekFragment.kt new file mode 100644 index 00000000..f8a518d3 --- /dev/null +++ b/src/com/android/calendar/month/MonthByWeekFragment.kt @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2010 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.month; + +import android.app.Activity; +import android.app.FragmentManager; +import android.app.LoaderManager; +import android.content.ContentUris; +import android.content.CursorLoader; +import android.content.Loader; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.drawable.StateListDrawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Instances; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; + +import com.android.calendar.CalendarController; +import com.android.calendar.CalendarController.EventInfo; +import com.android.calendar.CalendarController.EventType; +import com.android.calendar.CalendarController.ViewType; +import com.android.calendar.Event; +import com.android.calendar.R; +import com.android.calendar.Utils; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; + +public class MonthByWeekFragment extends SimpleDayPickerFragment implements + CalendarController.EventHandler, LoaderManager.LoaderCallbacks, OnScrollListener, + OnTouchListener { + private static final String TAG = "MonthFragment"; + private static final String TAG_EVENT_DIALOG = "event_dialog"; + + // Selection and selection args for adding event queries + private static final String WHERE_CALENDARS_VISIBLE = Calendars.VISIBLE + "=1"; + private static final String INSTANCES_SORT_ORDER = Instances.START_DAY + "," + + Instances.START_MINUTE + "," + Instances.TITLE; + protected static boolean mShowDetailsInMonth = false; + + protected float mMinimumTwoMonthFlingVelocity; + protected boolean mIsMiniMonth; + protected boolean mHideDeclined; + + protected int mFirstLoadedJulianDay; + protected int mLastLoadedJulianDay; + + private static final int WEEKS_BUFFER = 1; + // How long to wait after scroll stops before starting the loader + // Using scroll duration because scroll state changes don't update + // correctly when a scroll is triggered programmatically. + private static final int LOADER_DELAY = 200; + // The minimum time between requeries of the data if the db is + // changing + private static final int LOADER_THROTTLE_DELAY = 500; + + private CursorLoader mLoader; + private Uri mEventUri; + private final Time mDesiredDay = new Time(); + + private volatile boolean mShouldLoad = true; + private boolean mUserScrolled = false; + + private int mEventsLoadingDelay; + private boolean mShowCalendarControls; + private boolean mIsDetached; + + private final Runnable mTZUpdater = new Runnable() { + @Override + public void run() { + String tz = Utils.getTimeZone(mContext, mTZUpdater); + mSelectedDay.timezone = tz; + mSelectedDay.normalize(true); + mTempTime.timezone = tz; + mFirstDayOfMonth.timezone = tz; + mFirstDayOfMonth.normalize(true); + mFirstVisibleDay.timezone = tz; + mFirstVisibleDay.normalize(true); + if (mAdapter != null) { + mAdapter.refresh(); + } + } + }; + + + private final Runnable mUpdateLoader = new Runnable() { + @Override + public void run() { + synchronized (this) { + if (!mShouldLoad || mLoader == null) { + return; + } + // Stop any previous loads while we update the uri + stopLoader(); + + // Start the loader again + mEventUri = updateUri(); + + mLoader.setUri(mEventUri); + mLoader.startLoading(); + mLoader.onContentChanged(); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Started loader with uri: " + mEventUri); + } + } + } + }; + // Used to load the events when a delay is needed + Runnable mLoadingRunnable = new Runnable() { + @Override + public void run() { + if (!mIsDetached) { + mLoader = (CursorLoader) getLoaderManager().initLoader(0, null, + MonthByWeekFragment.this); + } + } + }; + + + /** + * Updates the uri used by the loader according to the current position of + * the listview. + * + * @return The new Uri to use + */ + private Uri updateUri() { + SimpleWeekView child = (SimpleWeekView) mListView.getChildAt(0); + if (child != null) { + int julianDay = child.getFirstJulianDay(); + mFirstLoadedJulianDay = julianDay; + } + // -1 to ensure we get all day events from any time zone + mTempTime.setJulianDay(mFirstLoadedJulianDay - 1); + long start = mTempTime.toMillis(true); + mLastLoadedJulianDay = mFirstLoadedJulianDay + (mNumWeeks + 2 * WEEKS_BUFFER) * 7; + // +1 to ensure we get all day events from any time zone + mTempTime.setJulianDay(mLastLoadedJulianDay + 1); + long end = mTempTime.toMillis(true); + + // Create a new uri with the updated times + Uri.Builder builder = Instances.CONTENT_URI.buildUpon(); + ContentUris.appendId(builder, start); + ContentUris.appendId(builder, end); + return builder.build(); + } + + // Extract range of julian days from URI + private void updateLoadedDays() { + List pathSegments = mEventUri.getPathSegments(); + int size = pathSegments.size(); + if (size <= 2) { + return; + } + long first = Long.parseLong(pathSegments.get(size - 2)); + long last = Long.parseLong(pathSegments.get(size - 1)); + mTempTime.set(first); + mFirstLoadedJulianDay = Time.getJulianDay(first, mTempTime.gmtoff); + mTempTime.set(last); + mLastLoadedJulianDay = Time.getJulianDay(last, mTempTime.gmtoff); + } + + protected String updateWhere() { + // TODO fix selection/selection args after b/3206641 is fixed + String where = WHERE_CALENDARS_VISIBLE; + if (mHideDeclined || !mShowDetailsInMonth) { + where += " AND " + Instances.SELF_ATTENDEE_STATUS + "!=" + + Attendees.ATTENDEE_STATUS_DECLINED; + } + return where; + } + + private void stopLoader() { + synchronized (mUpdateLoader) { + mHandler.removeCallbacks(mUpdateLoader); + if (mLoader != null) { + mLoader.stopLoading(); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Stopped loader from loading"); + } + } + } + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mTZUpdater.run(); + if (mAdapter != null) { + mAdapter.setSelectedDay(mSelectedDay); + } + mIsDetached = false; + + ViewConfiguration viewConfig = ViewConfiguration.get(activity); + mMinimumTwoMonthFlingVelocity = viewConfig.getScaledMaximumFlingVelocity() / 2; + Resources res = activity.getResources(); + mShowCalendarControls = Utils.getConfigBool(activity, R.bool.show_calendar_controls); + // Synchronized the loading time of the month's events with the animation of the + // calendar controls. + if (mShowCalendarControls) { + mEventsLoadingDelay = res.getInteger(R.integer.calendar_controls_animation_time); + } + mShowDetailsInMonth = res.getBoolean(R.bool.show_details_in_month); + } + + @Override + public void onDetach() { + mIsDetached = true; + super.onDetach(); + if (mShowCalendarControls) { + if (mListView != null) { + mListView.removeCallbacks(mLoadingRunnable); + } + } + } + + @Override + protected void setUpAdapter() { + mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); + mShowWeekNumber = Utils.getShowWeekNumber(mContext); + + HashMap weekParams = new HashMap(); + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks); + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, mShowWeekNumber ? 1 : 0); + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek); + weekParams.put(MonthByWeekAdapter.WEEK_PARAMS_IS_MINI, mIsMiniMonth ? 1 : 0); + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, + Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff)); + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_DAYS_PER_WEEK, mDaysPerWeek); + if (mAdapter == null) { + mAdapter = new MonthByWeekAdapter(getActivity(), weekParams); + mAdapter.registerDataSetObserver(mObserver); + } else { + mAdapter.updateParams(weekParams); + } + mAdapter.notifyDataSetChanged(); + } + + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v; + if (mIsMiniMonth) { + v = inflater.inflate(R.layout.month_by_week, container, false); + } else { + v = inflater.inflate(R.layout.full_month_by_week, container, false); + } + mDayNamesHeader = (ViewGroup) v.findViewById(R.id.day_names); + return v; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mListView.setSelector(new StateListDrawable()); + mListView.setOnTouchListener(this); + + if (!mIsMiniMonth) { + mListView.setBackgroundColor(getResources().getColor(R.color.month_bgcolor)); + } + + // To get a smoother transition when showing this fragment, delay loading of events until + // the fragment is expended fully and the calendar controls are gone. + if (mShowCalendarControls) { + mListView.postDelayed(mLoadingRunnable, mEventsLoadingDelay); + } else { + mLoader = (CursorLoader) getLoaderManager().initLoader(0, null, this); + } + mAdapter.setListView(mListView); + } + + public MonthByWeekFragment() { + this(System.currentTimeMillis(), true); + } + + public MonthByWeekFragment(long initialTime, boolean isMiniMonth) { + super(initialTime); + mIsMiniMonth = isMiniMonth; + } + + @Override + protected void setUpHeader() { + if (mIsMiniMonth) { + super.setUpHeader(); + return; + } + + mDayLabels = new String[7]; + for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { + mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString(i, + DateUtils.LENGTH_MEDIUM).toUpperCase(); + } + } + + // TODO + @Override + public Loader onCreateLoader(int id, Bundle args) { + if (mIsMiniMonth) { + return null; + } + CursorLoader loader; + synchronized (mUpdateLoader) { + mFirstLoadedJulianDay = + Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) + - (mNumWeeks * 7 / 2); + mEventUri = updateUri(); + String where = updateWhere(); + + loader = new CursorLoader( + getActivity(), mEventUri, Event.EVENT_PROJECTION, where, + null /* WHERE_CALENDARS_SELECTED_ARGS */, INSTANCES_SORT_ORDER); + loader.setUpdateThrottle(LOADER_THROTTLE_DELAY); + } + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Returning new loader with uri: " + mEventUri); + } + return loader; + } + + @Override + public void doResumeUpdates() { + mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); + mShowWeekNumber = Utils.getShowWeekNumber(mContext); + boolean prevHideDeclined = mHideDeclined; + mHideDeclined = Utils.getHideDeclinedEvents(mContext); + if (prevHideDeclined != mHideDeclined && mLoader != null) { + mLoader.setSelection(updateWhere()); + } + mDaysPerWeek = Utils.getDaysPerWeek(mContext); + updateHeader(); + mAdapter.setSelectedDay(mSelectedDay); + mTZUpdater.run(); + mTodayUpdater.run(); + goTo(mSelectedDay.toMillis(true), false, true, false); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + synchronized (mUpdateLoader) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Found " + data.getCount() + " cursor entries for uri " + mEventUri); + } + CursorLoader cLoader = (CursorLoader) loader; + if (mEventUri == null) { + mEventUri = cLoader.getUri(); + updateLoadedDays(); + } + if (cLoader.getUri().compareTo(mEventUri) != 0) { + // We've started a new query since this loader ran so ignore the + // result + return; + } + ArrayList events = new ArrayList(); + Event.buildEventsFromCursor( + events, data, mContext, mFirstLoadedJulianDay, mLastLoadedJulianDay); + ((MonthByWeekAdapter) mAdapter).setEvents(mFirstLoadedJulianDay, + mLastLoadedJulianDay - mFirstLoadedJulianDay + 1, events); + } + } + + @Override + public void onLoaderReset(Loader loader) { + } + + @Override + public void eventsChanged() { + // TODO remove this after b/3387924 is resolved + if (mLoader != null) { + mLoader.forceLoad(); + } + } + + @Override + public long getSupportedEventTypes() { + return EventType.GO_TO | EventType.EVENTS_CHANGED; + } + + @Override + public void handleEvent(EventInfo event) { + if (event.eventType == EventType.GO_TO) { + boolean animate = true; + if (mDaysPerWeek * mNumWeeks * 2 < Math.abs( + Time.getJulianDay(event.selectedTime.toMillis(true), event.selectedTime.gmtoff) + - Time.getJulianDay(mFirstVisibleDay.toMillis(true), mFirstVisibleDay.gmtoff) + - mDaysPerWeek * mNumWeeks / 2)) { + animate = false; + } + mDesiredDay.set(event.selectedTime); + mDesiredDay.normalize(true); + boolean animateToday = (event.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0; + boolean delayAnimation = goTo(event.selectedTime.toMillis(true), animate, true, false); + if (animateToday) { + // If we need to flash today start the animation after any + // movement from listView has ended. + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + ((MonthByWeekAdapter) mAdapter).animateToday(); + mAdapter.notifyDataSetChanged(); + } + }, delayAnimation ? GOTO_SCROLL_DURATION : 0); + } + } else if (event.eventType == EventType.EVENTS_CHANGED) { + eventsChanged(); + } + } + + @Override + protected void setMonthDisplayed(Time time, boolean updateHighlight) { + super.setMonthDisplayed(time, updateHighlight); + if (!mIsMiniMonth) { + boolean useSelected = false; + if (time.year == mDesiredDay.year && time.month == mDesiredDay.month) { + mSelectedDay.set(mDesiredDay); + mAdapter.setSelectedDay(mDesiredDay); + useSelected = true; + } else { + mSelectedDay.set(time); + mAdapter.setSelectedDay(time); + } + CalendarController controller = CalendarController.getInstance(mContext); + if (mSelectedDay.minute >= 30) { + mSelectedDay.minute = 30; + } else { + mSelectedDay.minute = 0; + } + long newTime = mSelectedDay.normalize(true); + if (newTime != controller.getTime() && mUserScrolled) { + long offset = useSelected ? 0 : DateUtils.WEEK_IN_MILLIS * mNumWeeks / 3; + controller.setTime(newTime + offset); + } + controller.sendEvent(this, EventType.UPDATE_TITLE, time, time, time, -1, + ViewType.CURRENT, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY + | DateUtils.FORMAT_SHOW_YEAR, null, null); + } + } + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + + synchronized (mUpdateLoader) { + if (scrollState != OnScrollListener.SCROLL_STATE_IDLE) { + mShouldLoad = false; + stopLoader(); + mDesiredDay.setToNow(); + } else { + mHandler.removeCallbacks(mUpdateLoader); + mShouldLoad = true; + mHandler.postDelayed(mUpdateLoader, LOADER_DELAY); + } + } + if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { + mUserScrolled = true; + } + + mScrollStateChangedRunnable.doScrollStateChange(view, scrollState); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + mDesiredDay.setToNow(); + return false; + } +} -- cgit v1.2.3 From 5a74b73119e1c4acd214566af08cae280a91c3be Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Mon, 28 Jun 2021 21:26:07 +0000 Subject: AOSP/Calendar - Inital conversion of MonthByWeekFragment This is how the automatic converter built into Android Studio converted MonthByWeekFragment.java to Kotlin. Change-Id: If269640b19e6b0c9381246d4ed34cffe49e1457a --- .../android/calendar/month/MonthByWeekFragment.kt | 584 ++++++++++----------- 1 file changed, 272 insertions(+), 312 deletions(-) diff --git a/src/com/android/calendar/month/MonthByWeekFragment.kt b/src/com/android/calendar/month/MonthByWeekFragment.kt index f8a518d3..df9f0da0 100644 --- a/src/com/android/calendar/month/MonthByWeekFragment.kt +++ b/src/com/android/calendar/month/MonthByWeekFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,140 +13,79 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar.month; - -import android.app.Activity; -import android.app.FragmentManager; -import android.app.LoaderManager; -import android.content.ContentUris; -import android.content.CursorLoader; -import android.content.Loader; -import android.content.res.Resources; -import android.database.Cursor; -import android.graphics.drawable.StateListDrawable; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Instances; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; - -import com.android.calendar.CalendarController; -import com.android.calendar.CalendarController.EventInfo; -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.CalendarController.ViewType; -import com.android.calendar.Event; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.HashMap; -import java.util.List; - -public class MonthByWeekFragment extends SimpleDayPickerFragment implements - CalendarController.EventHandler, LoaderManager.LoaderCallbacks, OnScrollListener, - OnTouchListener { - private static final String TAG = "MonthFragment"; - private static final String TAG_EVENT_DIALOG = "event_dialog"; - - // Selection and selection args for adding event queries - private static final String WHERE_CALENDARS_VISIBLE = Calendars.VISIBLE + "=1"; - private static final String INSTANCES_SORT_ORDER = Instances.START_DAY + "," - + Instances.START_MINUTE + "," + Instances.TITLE; - protected static boolean mShowDetailsInMonth = false; - - protected float mMinimumTwoMonthFlingVelocity; - protected boolean mIsMiniMonth; - protected boolean mHideDeclined; - - protected int mFirstLoadedJulianDay; - protected int mLastLoadedJulianDay; - - private static final int WEEKS_BUFFER = 1; - // How long to wait after scroll stops before starting the loader - // Using scroll duration because scroll state changes don't update - // correctly when a scroll is triggered programmatically. - private static final int LOADER_DELAY = 200; - // The minimum time between requeries of the data if the db is - // changing - private static final int LOADER_THROTTLE_DELAY = 500; - - private CursorLoader mLoader; - private Uri mEventUri; - private final Time mDesiredDay = new Time(); - - private volatile boolean mShouldLoad = true; - private boolean mUserScrolled = false; - - private int mEventsLoadingDelay; - private boolean mShowCalendarControls; - private boolean mIsDetached; - - private final Runnable mTZUpdater = new Runnable() { +package com.android.calendar.month + +import android.app.Activity + +class MonthByWeekFragment @JvmOverloads constructor( + initialTime: Long = System.currentTimeMillis(), + protected var mIsMiniMonth: Boolean = true +) : SimpleDayPickerFragment(initialTime), CalendarController.EventHandler, + LoaderManager.LoaderCallbacks, OnScrollListener, OnTouchListener { + protected var mMinimumTwoMonthFlingVelocity = 0f + protected var mHideDeclined = false + protected var mFirstLoadedJulianDay = 0 + protected var mLastLoadedJulianDay = 0 + private var mLoader: CursorLoader? = null + private var mEventUri: Uri? = null + private val mDesiredDay: Time = Time() + + @Volatile + private var mShouldLoad = true + private var mUserScrolled = false + private var mEventsLoadingDelay = 0 + private var mShowCalendarControls = false + private var mIsDetached = false + private val mTZUpdater: Runnable = object : Runnable() { @Override - public void run() { - String tz = Utils.getTimeZone(mContext, mTZUpdater); - mSelectedDay.timezone = tz; - mSelectedDay.normalize(true); - mTempTime.timezone = tz; - mFirstDayOfMonth.timezone = tz; - mFirstDayOfMonth.normalize(true); - mFirstVisibleDay.timezone = tz; - mFirstVisibleDay.normalize(true); + fun run() { + val tz: String = Utils.getTimeZone(mContext, this) + mSelectedDay.timezone = tz + mSelectedDay.normalize(true) + mTempTime.timezone = tz + mFirstDayOfMonth.timezone = tz + mFirstDayOfMonth.normalize(true) + mFirstVisibleDay.timezone = tz + mFirstVisibleDay.normalize(true) if (mAdapter != null) { - mAdapter.refresh(); + mAdapter.refresh() } } - }; - - - private final Runnable mUpdateLoader = new Runnable() { + } + private val mUpdateLoader: Runnable = object : Runnable() { @Override - public void run() { - synchronized (this) { + fun run() { + synchronized(this) { if (!mShouldLoad || mLoader == null) { - return; + return } // Stop any previous loads while we update the uri - stopLoader(); + stopLoader() // Start the loader again - mEventUri = updateUri(); - - mLoader.setUri(mEventUri); - mLoader.startLoading(); - mLoader.onContentChanged(); + mEventUri = updateUri() + mLoader.setUri(mEventUri) + mLoader.startLoading() + mLoader.onContentChanged() if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Started loader with uri: " + mEventUri); + Log.d(TAG, "Started loader with uri: $mEventUri") } } } - }; + } + // Used to load the events when a delay is needed - Runnable mLoadingRunnable = new Runnable() { + var mLoadingRunnable: Runnable = object : Runnable() { @Override - public void run() { + fun run() { if (!mIsDetached) { - mLoader = (CursorLoader) getLoaderManager().initLoader(0, null, - MonthByWeekFragment.this); + mLoader = getLoaderManager().initLoader( + 0, null, + this@MonthByWeekFragment + ) as CursorLoader } } - }; - + } /** * Updates the uri used by the loader according to the current position of @@ -154,341 +93,362 @@ public class MonthByWeekFragment extends SimpleDayPickerFragment implements * * @return The new Uri to use */ - private Uri updateUri() { - SimpleWeekView child = (SimpleWeekView) mListView.getChildAt(0); + private fun updateUri(): Uri { + val child: SimpleWeekView = mListView.getChildAt(0) as SimpleWeekView if (child != null) { - int julianDay = child.getFirstJulianDay(); - mFirstLoadedJulianDay = julianDay; + val julianDay: Int = child.getFirstJulianDay() + mFirstLoadedJulianDay = julianDay } // -1 to ensure we get all day events from any time zone - mTempTime.setJulianDay(mFirstLoadedJulianDay - 1); - long start = mTempTime.toMillis(true); - mLastLoadedJulianDay = mFirstLoadedJulianDay + (mNumWeeks + 2 * WEEKS_BUFFER) * 7; + mTempTime.setJulianDay(mFirstLoadedJulianDay - 1) + val start: Long = mTempTime.toMillis(true) + mLastLoadedJulianDay = mFirstLoadedJulianDay + (mNumWeeks + 2 * WEEKS_BUFFER) * 7 // +1 to ensure we get all day events from any time zone - mTempTime.setJulianDay(mLastLoadedJulianDay + 1); - long end = mTempTime.toMillis(true); + mTempTime.setJulianDay(mLastLoadedJulianDay + 1) + val end: Long = mTempTime.toMillis(true) // Create a new uri with the updated times - Uri.Builder builder = Instances.CONTENT_URI.buildUpon(); - ContentUris.appendId(builder, start); - ContentUris.appendId(builder, end); - return builder.build(); + val builder: Uri.Builder = Instances.CONTENT_URI.buildUpon() + ContentUris.appendId(builder, start) + ContentUris.appendId(builder, end) + return builder.build() } // Extract range of julian days from URI - private void updateLoadedDays() { - List pathSegments = mEventUri.getPathSegments(); - int size = pathSegments.size(); + private fun updateLoadedDays() { + val pathSegments: List = mEventUri.getPathSegments() + val size: Int = pathSegments.size() if (size <= 2) { - return; + return } - long first = Long.parseLong(pathSegments.get(size - 2)); - long last = Long.parseLong(pathSegments.get(size - 1)); - mTempTime.set(first); - mFirstLoadedJulianDay = Time.getJulianDay(first, mTempTime.gmtoff); - mTempTime.set(last); - mLastLoadedJulianDay = Time.getJulianDay(last, mTempTime.gmtoff); + val first: Long = Long.parseLong(pathSegments[size - 2]) + val last: Long = Long.parseLong(pathSegments[size - 1]) + mTempTime.set(first) + mFirstLoadedJulianDay = Time.getJulianDay(first, mTempTime.gmtoff) + mTempTime.set(last) + mLastLoadedJulianDay = Time.getJulianDay(last, mTempTime.gmtoff) } - protected String updateWhere() { + protected fun updateWhere(): String { // TODO fix selection/selection args after b/3206641 is fixed - String where = WHERE_CALENDARS_VISIBLE; + var where = WHERE_CALENDARS_VISIBLE if (mHideDeclined || !mShowDetailsInMonth) { - where += " AND " + Instances.SELF_ATTENDEE_STATUS + "!=" - + Attendees.ATTENDEE_STATUS_DECLINED; + where += (" AND " + Instances.SELF_ATTENDEE_STATUS.toString() + "!=" + + Attendees.ATTENDEE_STATUS_DECLINED) } - return where; + return where } - private void stopLoader() { - synchronized (mUpdateLoader) { - mHandler.removeCallbacks(mUpdateLoader); + private fun stopLoader() { + synchronized(mUpdateLoader) { + mHandler.removeCallbacks(mUpdateLoader) if (mLoader != null) { - mLoader.stopLoading(); + mLoader.stopLoading() if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Stopped loader from loading"); + Log.d(TAG, "Stopped loader from loading") } } } } @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mTZUpdater.run(); + fun onAttach(activity: Activity) { + super.onAttach(activity) + mTZUpdater.run() if (mAdapter != null) { - mAdapter.setSelectedDay(mSelectedDay); + mAdapter.setSelectedDay(mSelectedDay) } - mIsDetached = false; - - ViewConfiguration viewConfig = ViewConfiguration.get(activity); - mMinimumTwoMonthFlingVelocity = viewConfig.getScaledMaximumFlingVelocity() / 2; - Resources res = activity.getResources(); - mShowCalendarControls = Utils.getConfigBool(activity, R.bool.show_calendar_controls); + mIsDetached = false + val viewConfig: ViewConfiguration = ViewConfiguration.get(activity) + mMinimumTwoMonthFlingVelocity = viewConfig.getScaledMaximumFlingVelocity() / 2 + val res: Resources = activity.getResources() + mShowCalendarControls = Utils.getConfigBool(activity, R.bool.show_calendar_controls) // Synchronized the loading time of the month's events with the animation of the // calendar controls. if (mShowCalendarControls) { - mEventsLoadingDelay = res.getInteger(R.integer.calendar_controls_animation_time); + mEventsLoadingDelay = res.getInteger(R.integer.calendar_controls_animation_time) } - mShowDetailsInMonth = res.getBoolean(R.bool.show_details_in_month); + mShowDetailsInMonth = res.getBoolean(R.bool.show_details_in_month) } @Override - public void onDetach() { - mIsDetached = true; - super.onDetach(); + fun onDetach() { + mIsDetached = true + super.onDetach() if (mShowCalendarControls) { if (mListView != null) { - mListView.removeCallbacks(mLoadingRunnable); + mListView.removeCallbacks(mLoadingRunnable) } } } @Override - protected void setUpAdapter() { - mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - mShowWeekNumber = Utils.getShowWeekNumber(mContext); - - HashMap weekParams = new HashMap(); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, mShowWeekNumber ? 1 : 0); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek); - weekParams.put(MonthByWeekAdapter.WEEK_PARAMS_IS_MINI, mIsMiniMonth ? 1 : 0); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, - Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff)); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_DAYS_PER_WEEK, mDaysPerWeek); + protected fun setUpAdapter() { + mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext) + mShowWeekNumber = Utils.getShowWeekNumber(mContext) + val weekParams: HashMap = HashMap() + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks) + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, if (mShowWeekNumber) 1 else 0) + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek) + weekParams.put(MonthByWeekAdapter.WEEK_PARAMS_IS_MINI, if (mIsMiniMonth) 1 else 0) + weekParams.put( + SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, + Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) + ) + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_DAYS_PER_WEEK, mDaysPerWeek) if (mAdapter == null) { - mAdapter = new MonthByWeekAdapter(getActivity(), weekParams); - mAdapter.registerDataSetObserver(mObserver); + mAdapter = MonthByWeekAdapter(getActivity(), weekParams) + mAdapter.registerDataSetObserver(mObserver) } else { - mAdapter.updateParams(weekParams); + mAdapter.updateParams(weekParams) } - mAdapter.notifyDataSetChanged(); + mAdapter.notifyDataSetChanged() } @Override - public View onCreateView( - LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v; - if (mIsMiniMonth) { - v = inflater.inflate(R.layout.month_by_week, container, false); + fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + val v: View + v = if (mIsMiniMonth) { + inflater.inflate(R.layout.month_by_week, container, false) } else { - v = inflater.inflate(R.layout.full_month_by_week, container, false); + inflater.inflate(R.layout.full_month_by_week, container, false) } - mDayNamesHeader = (ViewGroup) v.findViewById(R.id.day_names); - return v; + mDayNamesHeader = v.findViewById(R.id.day_names) as ViewGroup + return v } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - mListView.setSelector(new StateListDrawable()); - mListView.setOnTouchListener(this); - + fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + mListView.setSelector(StateListDrawable()) + mListView.setOnTouchListener(this) if (!mIsMiniMonth) { - mListView.setBackgroundColor(getResources().getColor(R.color.month_bgcolor)); + mListView.setBackgroundColor(getResources().getColor(R.color.month_bgcolor)) } // To get a smoother transition when showing this fragment, delay loading of events until // the fragment is expended fully and the calendar controls are gone. if (mShowCalendarControls) { - mListView.postDelayed(mLoadingRunnable, mEventsLoadingDelay); + mListView.postDelayed(mLoadingRunnable, mEventsLoadingDelay) } else { - mLoader = (CursorLoader) getLoaderManager().initLoader(0, null, this); + mLoader = getLoaderManager().initLoader(0, null, this) as CursorLoader } - mAdapter.setListView(mListView); - } - - public MonthByWeekFragment() { - this(System.currentTimeMillis(), true); - } - - public MonthByWeekFragment(long initialTime, boolean isMiniMonth) { - super(initialTime); - mIsMiniMonth = isMiniMonth; + mAdapter.setListView(mListView) } @Override - protected void setUpHeader() { + protected fun setUpHeader() { if (mIsMiniMonth) { - super.setUpHeader(); - return; + super.setUpHeader() + return } - - mDayLabels = new String[7]; - for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { - mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString(i, - DateUtils.LENGTH_MEDIUM).toUpperCase(); + mDayLabels = arrayOfNulls(7) + for (i in Calendar.SUNDAY..Calendar.SATURDAY) { + mDayLabels.get(i - Calendar.SUNDAY) = DateUtils.getDayOfWeekString( + i, + DateUtils.LENGTH_MEDIUM + ).toUpperCase() } } // TODO @Override - public Loader onCreateLoader(int id, Bundle args) { + fun onCreateLoader(id: Int, args: Bundle?): Loader? { if (mIsMiniMonth) { - return null; + return null } - CursorLoader loader; - synchronized (mUpdateLoader) { + var loader: CursorLoader + synchronized(mUpdateLoader) { mFirstLoadedJulianDay = - Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) - - (mNumWeeks * 7 / 2); - mEventUri = updateUri(); - String where = updateWhere(); - - loader = new CursorLoader( - getActivity(), mEventUri, Event.EVENT_PROJECTION, where, - null /* WHERE_CALENDARS_SELECTED_ARGS */, INSTANCES_SORT_ORDER); - loader.setUpdateThrottle(LOADER_THROTTLE_DELAY); + (Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) + - mNumWeeks * 7 / 2) + mEventUri = updateUri() + val where = updateWhere() + loader = CursorLoader( + getActivity(), mEventUri, Event.EVENT_PROJECTION, where, + null /* WHERE_CALENDARS_SELECTED_ARGS */, INSTANCES_SORT_ORDER + ) + loader.setUpdateThrottle(LOADER_THROTTLE_DELAY) } if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Returning new loader with uri: " + mEventUri); + Log.d(TAG, "Returning new loader with uri: $mEventUri") } - return loader; + return loader } @Override - public void doResumeUpdates() { - mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - mShowWeekNumber = Utils.getShowWeekNumber(mContext); - boolean prevHideDeclined = mHideDeclined; - mHideDeclined = Utils.getHideDeclinedEvents(mContext); + fun doResumeUpdates() { + mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext) + mShowWeekNumber = Utils.getShowWeekNumber(mContext) + val prevHideDeclined = mHideDeclined + mHideDeclined = Utils.getHideDeclinedEvents(mContext) if (prevHideDeclined != mHideDeclined && mLoader != null) { - mLoader.setSelection(updateWhere()); + mLoader.setSelection(updateWhere()) } - mDaysPerWeek = Utils.getDaysPerWeek(mContext); - updateHeader(); - mAdapter.setSelectedDay(mSelectedDay); - mTZUpdater.run(); - mTodayUpdater.run(); - goTo(mSelectedDay.toMillis(true), false, true, false); + mDaysPerWeek = Utils.getDaysPerWeek(mContext) + updateHeader() + mAdapter.setSelectedDay(mSelectedDay) + mTZUpdater.run() + mTodayUpdater.run() + goTo(mSelectedDay.toMillis(true), false, true, false) } @Override - public void onLoadFinished(Loader loader, Cursor data) { - synchronized (mUpdateLoader) { + fun onLoadFinished(loader: Loader, data: Cursor) { + synchronized(mUpdateLoader) { if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Found " + data.getCount() + " cursor entries for uri " + mEventUri); + Log.d( + TAG, + "Found " + data.getCount().toString() + " cursor entries for uri " + mEventUri + ) } - CursorLoader cLoader = (CursorLoader) loader; + val cLoader: CursorLoader = loader as CursorLoader if (mEventUri == null) { - mEventUri = cLoader.getUri(); - updateLoadedDays(); + mEventUri = cLoader.getUri() + updateLoadedDays() } - if (cLoader.getUri().compareTo(mEventUri) != 0) { + if (cLoader.getUri().compareTo(mEventUri) !== 0) { // We've started a new query since this loader ran so ignore the // result - return; + return } - ArrayList events = new ArrayList(); + val events: ArrayList = ArrayList() Event.buildEventsFromCursor( - events, data, mContext, mFirstLoadedJulianDay, mLastLoadedJulianDay); - ((MonthByWeekAdapter) mAdapter).setEvents(mFirstLoadedJulianDay, - mLastLoadedJulianDay - mFirstLoadedJulianDay + 1, events); + events, data, mContext, mFirstLoadedJulianDay, mLastLoadedJulianDay + ) + (mAdapter as MonthByWeekAdapter).setEvents( + mFirstLoadedJulianDay, + mLastLoadedJulianDay - mFirstLoadedJulianDay + 1, events + ) } } @Override - public void onLoaderReset(Loader loader) { + fun onLoaderReset(loader: Loader?) { } @Override - public void eventsChanged() { + fun eventsChanged() { // TODO remove this after b/3387924 is resolved if (mLoader != null) { - mLoader.forceLoad(); + mLoader.forceLoad() } } - @Override - public long getSupportedEventTypes() { - return EventType.GO_TO | EventType.EVENTS_CHANGED; - } + @get:Override val supportedEventTypes: Long + get() = EventType.GO_TO or EventType.EVENTS_CHANGED @Override - public void handleEvent(EventInfo event) { - if (event.eventType == EventType.GO_TO) { - boolean animate = true; + fun handleEvent(event: EventInfo) { + if (event.eventType === EventType.GO_TO) { + var animate = true if (mDaysPerWeek * mNumWeeks * 2 < Math.abs( Time.getJulianDay(event.selectedTime.toMillis(true), event.selectedTime.gmtoff) - - Time.getJulianDay(mFirstVisibleDay.toMillis(true), mFirstVisibleDay.gmtoff) - - mDaysPerWeek * mNumWeeks / 2)) { - animate = false; + - Time.getJulianDay(mFirstVisibleDay.toMillis(true), mFirstVisibleDay.gmtoff) + - mDaysPerWeek * mNumWeeks / 2 + ) + ) { + animate = false } - mDesiredDay.set(event.selectedTime); - mDesiredDay.normalize(true); - boolean animateToday = (event.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0; - boolean delayAnimation = goTo(event.selectedTime.toMillis(true), animate, true, false); + mDesiredDay.set(event.selectedTime) + mDesiredDay.normalize(true) + val animateToday = event.extraLong and CalendarController.EXTRA_GOTO_TODAY !== 0 + val delayAnimation: Boolean = + goTo(event.selectedTime.toMillis(true), animate, true, false) if (animateToday) { // If we need to flash today start the animation after any // movement from listView has ended. - mHandler.postDelayed(new Runnable() { + mHandler.postDelayed(object : Runnable() { @Override - public void run() { - ((MonthByWeekAdapter) mAdapter).animateToday(); - mAdapter.notifyDataSetChanged(); + fun run() { + (mAdapter as MonthByWeekAdapter).animateToday() + mAdapter.notifyDataSetChanged() } - }, delayAnimation ? GOTO_SCROLL_DURATION : 0); + }, if (delayAnimation) GOTO_SCROLL_DURATION else 0) } - } else if (event.eventType == EventType.EVENTS_CHANGED) { - eventsChanged(); + } else if (event.eventType === EventType.EVENTS_CHANGED) { + eventsChanged() } } @Override - protected void setMonthDisplayed(Time time, boolean updateHighlight) { - super.setMonthDisplayed(time, updateHighlight); + protected fun setMonthDisplayed(time: Time, updateHighlight: Boolean) { + super.setMonthDisplayed(time, updateHighlight) if (!mIsMiniMonth) { - boolean useSelected = false; - if (time.year == mDesiredDay.year && time.month == mDesiredDay.month) { - mSelectedDay.set(mDesiredDay); - mAdapter.setSelectedDay(mDesiredDay); - useSelected = true; + var useSelected = false + if (time.year === mDesiredDay.year && time.month === mDesiredDay.month) { + mSelectedDay.set(mDesiredDay) + mAdapter.setSelectedDay(mDesiredDay) + useSelected = true } else { - mSelectedDay.set(time); - mAdapter.setSelectedDay(time); + mSelectedDay.set(time) + mAdapter.setSelectedDay(time) } - CalendarController controller = CalendarController.getInstance(mContext); + val controller: CalendarController = CalendarController.getInstance(mContext) if (mSelectedDay.minute >= 30) { - mSelectedDay.minute = 30; + mSelectedDay.minute = 30 } else { - mSelectedDay.minute = 0; + mSelectedDay.minute = 0 } - long newTime = mSelectedDay.normalize(true); + val newTime: Long = mSelectedDay.normalize(true) if (newTime != controller.getTime() && mUserScrolled) { - long offset = useSelected ? 0 : DateUtils.WEEK_IN_MILLIS * mNumWeeks / 3; - controller.setTime(newTime + offset); + val offset: Long = + if (useSelected) 0 else DateUtils.WEEK_IN_MILLIS * mNumWeeks / 3.toLong() + controller.setTime(newTime + offset) } - controller.sendEvent(this, EventType.UPDATE_TITLE, time, time, time, -1, - ViewType.CURRENT, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY - | DateUtils.FORMAT_SHOW_YEAR, null, null); + controller.sendEvent( + this, EventType.UPDATE_TITLE, time, time, time, -1, + ViewType.CURRENT, DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_MONTH_DAY + or DateUtils.FORMAT_SHOW_YEAR, null, null + ) } } @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - - synchronized (mUpdateLoader) { + fun onScrollStateChanged(view: AbsListView?, scrollState: Int) { + synchronized(mUpdateLoader) { if (scrollState != OnScrollListener.SCROLL_STATE_IDLE) { - mShouldLoad = false; - stopLoader(); - mDesiredDay.setToNow(); + mShouldLoad = false + stopLoader() + mDesiredDay.setToNow() } else { - mHandler.removeCallbacks(mUpdateLoader); - mShouldLoad = true; - mHandler.postDelayed(mUpdateLoader, LOADER_DELAY); + mHandler.removeCallbacks(mUpdateLoader) + mShouldLoad = true + mHandler.postDelayed(mUpdateLoader, LOADER_DELAY) } } if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { - mUserScrolled = true; + mUserScrolled = true } - - mScrollStateChangedRunnable.doScrollStateChange(view, scrollState); + mScrollStateChangedRunnable.doScrollStateChange(view, scrollState) } @Override - public boolean onTouch(View v, MotionEvent event) { - mDesiredDay.setToNow(); - return false; + fun onTouch(v: View?, event: MotionEvent?): Boolean { + mDesiredDay.setToNow() + return false + } + + companion object { + private const val TAG = "MonthFragment" + private const val TAG_EVENT_DIALOG = "event_dialog" + + // Selection and selection args for adding event queries + private val WHERE_CALENDARS_VISIBLE: String = Calendars.VISIBLE.toString() + "=1" + private val INSTANCES_SORT_ORDER: String = (Instances.START_DAY.toString() + "," + + Instances.START_MINUTE + "," + Instances.TITLE) + protected var mShowDetailsInMonth = false + private const val WEEKS_BUFFER = 1 + + // How long to wait after scroll stops before starting the loader + // Using scroll duration because scroll state changes don't update + // correctly when a scroll is triggered programmatically. + private const val LOADER_DELAY = 200 + + // The minimum time between requeries of the data if the db is + // changing + private const val LOADER_THROTTLE_DELAY = 500 } -} +} \ No newline at end of file -- cgit v1.2.3 From 2882e5f0c09de852ef9ac6a5234b45ecbf622687 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Mon, 28 Jun 2021 22:06:40 +0000 Subject: AOSP/Calendar - MonthByWeekFragment fully converted with bp file This is the fully touched-up converted Kotlin file after the first instance of Android Studio's automatic converter was run. The override modifier had to had to be manually added and certain object references had to made nullable to avoid runtime errrors. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.166 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 8m 5s Total aggregated tests run time: 8m 5s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.77 tests/sec [376 tests / 485544 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 9497 ms || clean = 2144 ms Total preparation time: 9s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 11m 10s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: I51838ac07c68c7360ee0368a55b152814669d350 --- Android.bp | 1 + .../android/calendar/month/MonthByWeekFragment.kt | 177 +++++++++++++-------- 2 files changed, 110 insertions(+), 68 deletions(-) diff --git a/Android.bp b/Android.bp index 3ea5e1fe..130217aa 100644 --- a/Android.bp +++ b/Android.bp @@ -27,6 +27,7 @@ exclude_srcsd = [ "src/**/calendar/alerts/InitAlarmsService.java", "src/**/calendar/alerts/NotificationMgr.java", "src/**/calendar/alerts/QuickResponseActivity.java", + "src/**/calendar/month/MonthByWeekFragment.java", "src/**/calendar/widget/CalendarAppWidgetModel.java", "src/**/calendar/CalendarController.java", "src/**/calendar/DayOfMonthDrawable.java", diff --git a/src/com/android/calendar/month/MonthByWeekFragment.kt b/src/com/android/calendar/month/MonthByWeekFragment.kt index df9f0da0..d03b7464 100644 --- a/src/com/android/calendar/month/MonthByWeekFragment.kt +++ b/src/com/android/calendar/month/MonthByWeekFragment.kt @@ -16,6 +16,41 @@ package com.android.calendar.month import android.app.Activity +import android.app.LoaderManager +import android.content.ContentUris +import android.content.CursorLoader +import android.content.Loader +import android.content.res.Resources +import android.database.Cursor +import android.graphics.drawable.StateListDrawable +import android.net.Uri +import android.os.Bundle +import android.provider.CalendarContract.Attendees +import android.provider.CalendarContract.Calendars +import android.provider.CalendarContract.Instances +import android.text.format.DateUtils +import android.text.format.Time +import android.util.Log +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.View.OnTouchListener +import android.view.ViewConfiguration +import android.view.ViewGroup +import android.widget.AbsListView +import android.widget.AbsListView.OnScrollListener + +import com.android.calendar.CalendarController +import com.android.calendar.CalendarController.EventInfo +import com.android.calendar.CalendarController.EventType +import com.android.calendar.CalendarController.ViewType +import com.android.calendar.Event +import com.android.calendar.R +import com.android.calendar.Utils + +import java.util.ArrayList +import java.util.Calendar +import java.util.HashMap class MonthByWeekFragment @JvmOverloads constructor( initialTime: Long = System.currentTimeMillis(), @@ -36,10 +71,10 @@ class MonthByWeekFragment @JvmOverloads constructor( private var mEventsLoadingDelay = 0 private var mShowCalendarControls = false private var mIsDetached = false - private val mTZUpdater: Runnable = object : Runnable() { + private val mTZUpdater: Runnable = object : Runnable { @Override - fun run() { - val tz: String = Utils.getTimeZone(mContext, this) + override fun run() { + val tz: String? = Utils.getTimeZone(mContext, this) mSelectedDay.timezone = tz mSelectedDay.normalize(true) mTempTime.timezone = tz @@ -52,9 +87,9 @@ class MonthByWeekFragment @JvmOverloads constructor( } } } - private val mUpdateLoader: Runnable = object : Runnable() { + private val mUpdateLoader: Runnable = object : Runnable { @Override - fun run() { + override fun run() { synchronized(this) { if (!mShouldLoad || mLoader == null) { return @@ -64,9 +99,9 @@ class MonthByWeekFragment @JvmOverloads constructor( // Start the loader again mEventUri = updateUri() - mLoader.setUri(mEventUri) - mLoader.startLoading() - mLoader.onContentChanged() + mLoader?.setUri(mEventUri) + mLoader?.startLoading() + mLoader?.onContentChanged() if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Started loader with uri: $mEventUri") } @@ -75,9 +110,9 @@ class MonthByWeekFragment @JvmOverloads constructor( } // Used to load the events when a delay is needed - var mLoadingRunnable: Runnable = object : Runnable() { + var mLoadingRunnable: Runnable = object : Runnable { @Override - fun run() { + override fun run() { if (!mIsDetached) { mLoader = getLoaderManager().initLoader( 0, null, @@ -94,9 +129,9 @@ class MonthByWeekFragment @JvmOverloads constructor( * @return The new Uri to use */ private fun updateUri(): Uri { - val child: SimpleWeekView = mListView.getChildAt(0) as SimpleWeekView + val child: SimpleWeekView? = mListView.getChildAt(0) as? SimpleWeekView if (child != null) { - val julianDay: Int = child.getFirstJulianDay() + val julianDay: Int = child?.getFirstJulianDay() mFirstLoadedJulianDay = julianDay } // -1 to ensure we get all day events from any time zone @@ -116,13 +151,13 @@ class MonthByWeekFragment @JvmOverloads constructor( // Extract range of julian days from URI private fun updateLoadedDays() { - val pathSegments: List = mEventUri.getPathSegments() - val size: Int = pathSegments.size() + val pathSegments = mEventUri?.getPathSegments() + val size: Int = pathSegments?.size as Int if (size <= 2) { return } - val first: Long = Long.parseLong(pathSegments[size - 2]) - val last: Long = Long.parseLong(pathSegments[size - 1]) + val first: Long = (pathSegments!![size - 2])?.toLong() as Long + val last: Long = (pathSegments!![size - 1])?.toLong() as Long mTempTime.set(first) mFirstLoadedJulianDay = Time.getJulianDay(first, mTempTime.gmtoff) mTempTime.set(last) @@ -133,8 +168,8 @@ class MonthByWeekFragment @JvmOverloads constructor( // TODO fix selection/selection args after b/3206641 is fixed var where = WHERE_CALENDARS_VISIBLE if (mHideDeclined || !mShowDetailsInMonth) { - where += (" AND " + Instances.SELF_ATTENDEE_STATUS.toString() + "!=" - + Attendees.ATTENDEE_STATUS_DECLINED) + where += (" AND " + Instances.SELF_ATTENDEE_STATUS.toString() + "!=" + + Attendees.ATTENDEE_STATUS_DECLINED) } return where } @@ -143,7 +178,7 @@ class MonthByWeekFragment @JvmOverloads constructor( synchronized(mUpdateLoader) { mHandler.removeCallbacks(mUpdateLoader) if (mLoader != null) { - mLoader.stopLoading() + mLoader?.stopLoading() if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Stopped loader from loading") } @@ -152,7 +187,7 @@ class MonthByWeekFragment @JvmOverloads constructor( } @Override - fun onAttach(activity: Activity) { + override fun onAttach(activity: Activity) { super.onAttach(activity) mTZUpdater.run() if (mAdapter != null) { @@ -160,7 +195,7 @@ class MonthByWeekFragment @JvmOverloads constructor( } mIsDetached = false val viewConfig: ViewConfiguration = ViewConfiguration.get(activity) - mMinimumTwoMonthFlingVelocity = viewConfig.getScaledMaximumFlingVelocity() / 2 + mMinimumTwoMonthFlingVelocity = viewConfig.getScaledMaximumFlingVelocity().toFloat() / 2f val res: Resources = activity.getResources() mShowCalendarControls = Utils.getConfigBool(activity, R.bool.show_calendar_controls) // Synchronized the loading time of the month's events with the animation of the @@ -172,7 +207,7 @@ class MonthByWeekFragment @JvmOverloads constructor( } @Override - fun onDetach() { + override fun onDetach() { mIsDetached = true super.onDetach() if (mShowCalendarControls) { @@ -183,10 +218,10 @@ class MonthByWeekFragment @JvmOverloads constructor( } @Override - protected fun setUpAdapter() { + protected override fun setUpAdapter() { mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext) mShowWeekNumber = Utils.getShowWeekNumber(mContext) - val weekParams: HashMap = HashMap() + val weekParams: HashMap = HashMap() weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks) weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, if (mShowWeekNumber) 1 else 0) weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek) @@ -206,8 +241,10 @@ class MonthByWeekFragment @JvmOverloads constructor( } @Override - fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? ): View { val v: View v = if (mIsMiniMonth) { @@ -220,7 +257,7 @@ class MonthByWeekFragment @JvmOverloads constructor( } @Override - fun onActivityCreated(savedInstanceState: Bundle?) { + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) mListView.setSelector(StateListDrawable()) mListView.setOnTouchListener(this) @@ -231,7 +268,7 @@ class MonthByWeekFragment @JvmOverloads constructor( // To get a smoother transition when showing this fragment, delay loading of events until // the fragment is expended fully and the calendar controls are gone. if (mShowCalendarControls) { - mListView.postDelayed(mLoadingRunnable, mEventsLoadingDelay) + mListView.postDelayed(mLoadingRunnable, mEventsLoadingDelay.toLong()) } else { mLoader = getLoaderManager().initLoader(0, null, this) as CursorLoader } @@ -239,14 +276,14 @@ class MonthByWeekFragment @JvmOverloads constructor( } @Override - protected fun setUpHeader() { + protected override fun setUpHeader() { if (mIsMiniMonth) { super.setUpHeader() return } mDayLabels = arrayOfNulls(7) for (i in Calendar.SUNDAY..Calendar.SATURDAY) { - mDayLabels.get(i - Calendar.SUNDAY) = DateUtils.getDayOfWeekString( + mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString( i, DateUtils.LENGTH_MEDIUM ).toUpperCase() @@ -255,22 +292,22 @@ class MonthByWeekFragment @JvmOverloads constructor( // TODO @Override - fun onCreateLoader(id: Int, args: Bundle?): Loader? { + override fun onCreateLoader(id: Int, args: Bundle?): Loader? { if (mIsMiniMonth) { return null } - var loader: CursorLoader + var loader: CursorLoader? synchronized(mUpdateLoader) { mFirstLoadedJulianDay = - (Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) - - mNumWeeks * 7 / 2) + (Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) - + mNumWeeks * 7 / 2) mEventUri = updateUri() val where = updateWhere() loader = CursorLoader( getActivity(), mEventUri, Event.EVENT_PROJECTION, where, null /* WHERE_CALENDARS_SELECTED_ARGS */, INSTANCES_SORT_ORDER ) - loader.setUpdateThrottle(LOADER_THROTTLE_DELAY) + loader?.setUpdateThrottle(LOADER_THROTTLE_DELAY.toLong()) } if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Returning new loader with uri: $mEventUri") @@ -279,13 +316,13 @@ class MonthByWeekFragment @JvmOverloads constructor( } @Override - fun doResumeUpdates() { + override fun doResumeUpdates() { mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext) mShowWeekNumber = Utils.getShowWeekNumber(mContext) val prevHideDeclined = mHideDeclined mHideDeclined = Utils.getHideDeclinedEvents(mContext) if (prevHideDeclined != mHideDeclined && mLoader != null) { - mLoader.setSelection(updateWhere()) + mLoader?.setSelection(updateWhere()) } mDaysPerWeek = Utils.getDaysPerWeek(mContext) updateHeader() @@ -296,12 +333,12 @@ class MonthByWeekFragment @JvmOverloads constructor( } @Override - fun onLoadFinished(loader: Loader, data: Cursor) { + override fun onLoadFinished(loader: Loader?, data: Cursor?) { synchronized(mUpdateLoader) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d( TAG, - "Found " + data.getCount().toString() + " cursor entries for uri " + mEventUri + "Found " + data?.getCount()?.toString() + " cursor entries for uri " + mEventUri ) } val cLoader: CursorLoader = loader as CursorLoader @@ -314,7 +351,7 @@ class MonthByWeekFragment @JvmOverloads constructor( // result return } - val events: ArrayList = ArrayList() + val events: ArrayList? = ArrayList() Event.buildEventsFromCursor( events, data, mContext, mFirstLoadedJulianDay, mLastLoadedJulianDay ) @@ -326,59 +363,62 @@ class MonthByWeekFragment @JvmOverloads constructor( } @Override - fun onLoaderReset(loader: Loader?) { + override fun onLoaderReset(loader: Loader?) { } @Override - fun eventsChanged() { + override fun eventsChanged() { // TODO remove this after b/3387924 is resolved if (mLoader != null) { - mLoader.forceLoad() + mLoader?.forceLoad() } } - @get:Override val supportedEventTypes: Long + @get:Override override val supportedEventTypes: Long get() = EventType.GO_TO or EventType.EVENTS_CHANGED @Override - fun handleEvent(event: EventInfo) { - if (event.eventType === EventType.GO_TO) { + override fun handleEvent(event: CalendarController.EventInfo?) { + if (event?.eventType === EventType.GO_TO) { var animate = true if (mDaysPerWeek * mNumWeeks * 2 < Math.abs( - Time.getJulianDay(event.selectedTime.toMillis(true), event.selectedTime.gmtoff) - - Time.getJulianDay(mFirstVisibleDay.toMillis(true), mFirstVisibleDay.gmtoff) - - mDaysPerWeek * mNumWeeks / 2 + Time.getJulianDay(event?.selectedTime?.toMillis(true) as Long, + event?.selectedTime?.gmtoff as Long) - + Time.getJulianDay(mFirstVisibleDay?.toMillis(true) as Long, + mFirstVisibleDay?.gmtoff as Long) - + mDaysPerWeek * mNumWeeks / 2L ) ) { animate = false } - mDesiredDay.set(event.selectedTime) + mDesiredDay.set(event?.selectedTime) mDesiredDay.normalize(true) - val animateToday = event.extraLong and CalendarController.EXTRA_GOTO_TODAY !== 0 + val animateToday = event?.extraLong and + CalendarController.EXTRA_GOTO_TODAY.toLong() != 0L val delayAnimation: Boolean = - goTo(event.selectedTime.toMillis(true), animate, true, false) + goTo(event?.selectedTime?.toMillis(true)?.toLong() as Long, animate, true, false) if (animateToday) { // If we need to flash today start the animation after any // movement from listView has ended. - mHandler.postDelayed(object : Runnable() { + mHandler.postDelayed(object : Runnable { @Override - fun run() { + override fun run() { (mAdapter as MonthByWeekAdapter).animateToday() mAdapter.notifyDataSetChanged() } - }, if (delayAnimation) GOTO_SCROLL_DURATION else 0) + }, if (delayAnimation) GOTO_SCROLL_DURATION.toLong() else 0L) } - } else if (event.eventType === EventType.EVENTS_CHANGED) { + } else if (event?.eventType == EventType.EVENTS_CHANGED) { eventsChanged() } } @Override - protected fun setMonthDisplayed(time: Time, updateHighlight: Boolean) { + protected override fun setMonthDisplayed(time: Time, updateHighlight: Boolean) { super.setMonthDisplayed(time, updateHighlight) if (!mIsMiniMonth) { var useSelected = false - if (time.year === mDesiredDay.year && time.month === mDesiredDay.month) { + if (time.year == mDesiredDay.year && time.month === mDesiredDay.month) { mSelectedDay.set(mDesiredDay) mAdapter.setSelectedDay(mDesiredDay) useSelected = true @@ -393,21 +433,22 @@ class MonthByWeekFragment @JvmOverloads constructor( mSelectedDay.minute = 0 } val newTime: Long = mSelectedDay.normalize(true) - if (newTime != controller.getTime() && mUserScrolled) { + if (newTime != controller.time && mUserScrolled) { val offset: Long = if (useSelected) 0 else DateUtils.WEEK_IN_MILLIS * mNumWeeks / 3.toLong() - controller.setTime(newTime + offset) + controller.time = (newTime + offset) } controller.sendEvent( - this, EventType.UPDATE_TITLE, time, time, time, -1, - ViewType.CURRENT, DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_MONTH_DAY - or DateUtils.FORMAT_SHOW_YEAR, null, null + this as Object?, EventType.UPDATE_TITLE, time, time, time, -1, + ViewType.CURRENT, DateUtils.FORMAT_SHOW_DATE.toLong() or + DateUtils.FORMAT_NO_MONTH_DAY.toLong() or + DateUtils.FORMAT_SHOW_YEAR.toLong(), null, null ) } } @Override - fun onScrollStateChanged(view: AbsListView?, scrollState: Int) { + override fun onScrollStateChanged(view: AbsListView?, scrollState: Int) { synchronized(mUpdateLoader) { if (scrollState != OnScrollListener.SCROLL_STATE_IDLE) { mShouldLoad = false @@ -416,7 +457,7 @@ class MonthByWeekFragment @JvmOverloads constructor( } else { mHandler.removeCallbacks(mUpdateLoader) mShouldLoad = true - mHandler.postDelayed(mUpdateLoader, LOADER_DELAY) + mHandler.postDelayed(mUpdateLoader, LOADER_DELAY.toLong()) } } if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { @@ -426,7 +467,7 @@ class MonthByWeekFragment @JvmOverloads constructor( } @Override - fun onTouch(v: View?, event: MotionEvent?): Boolean { + override fun onTouch(v: View?, event: MotionEvent?): Boolean { mDesiredDay.setToNow() return false } @@ -437,8 +478,8 @@ class MonthByWeekFragment @JvmOverloads constructor( // Selection and selection args for adding event queries private val WHERE_CALENDARS_VISIBLE: String = Calendars.VISIBLE.toString() + "=1" - private val INSTANCES_SORT_ORDER: String = (Instances.START_DAY.toString() + "," - + Instances.START_MINUTE + "," + Instances.TITLE) + private val INSTANCES_SORT_ORDER: String = (Instances.START_DAY.toString() + "," + + Instances.START_MINUTE + "," + Instances.TITLE) protected var mShowDetailsInMonth = false private const val WEEKS_BUFFER = 1 -- cgit v1.2.3 From 2b78e72bb619d86763a106db5a70ff5f7f7447a3 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Tue, 29 Jun 2021 00:14:36 +0000 Subject: AOSP/Calendar - Add Kotlin copy of SimpleDayPickerFragment.java Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: Ib6921677f4b7bb13d60c17fc359275a58c13a185 --- .../calendar/month/SimpleDayPickerFragment.kt | 612 +++++++++++++++++++++ 1 file changed, 612 insertions(+) create mode 100644 src/com/android/calendar/month/SimpleDayPickerFragment.kt diff --git a/src/com/android/calendar/month/SimpleDayPickerFragment.kt b/src/com/android/calendar/month/SimpleDayPickerFragment.kt new file mode 100644 index 00000000..2efae6a9 --- /dev/null +++ b/src/com/android/calendar/month/SimpleDayPickerFragment.kt @@ -0,0 +1,612 @@ +/* + * Copyright (C) 2010 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.month; + +import com.android.calendar.R; +import com.android.calendar.Utils; + +import android.app.Activity; +import android.app.ListFragment; +import android.content.Context; +import android.content.res.Resources; +import android.database.DataSetObserver; +import android.os.Bundle; +import android.os.Handler; +import android.text.TextUtils; +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.ViewConfiguration; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.Calendar; +import java.util.HashMap; +import java.util.Locale; + +/** + *

+ * This displays a titled list of weeks with selectable days. It can be + * configured to display the week number, start the week on a given day, show a + * reduced number of days, or display an arbitrary number of weeks at a time. By + * overriding methods and changing variables this fragment can be customized to + * easily display a month selection component in a given style. + *

+ */ +public class SimpleDayPickerFragment extends ListFragment implements OnScrollListener { + + private static final String TAG = "MonthFragment"; + private static final String KEY_CURRENT_TIME = "current_time"; + + // Affects when the month selection will change while scrolling up + protected static final int SCROLL_HYST_WEEKS = 2; + // How long the GoTo fling animation should last + protected static final int GOTO_SCROLL_DURATION = 500; + // How long to wait after receiving an onScrollStateChanged notification + // before acting on it + protected static final int SCROLL_CHANGE_DELAY = 40; + // The number of days to display in each week + public static final int DAYS_PER_WEEK = 7; + // The size of the month name displayed above the week list + protected static final int MINI_MONTH_NAME_TEXT_SIZE = 18; + public static int LIST_TOP_OFFSET = -1; // so that the top line will be under the separator + protected int WEEK_MIN_VISIBLE_HEIGHT = 12; + protected int BOTTOM_BUFFER = 20; + protected int mSaturdayColor = 0; + protected int mSundayColor = 0; + protected int mDayNameColor = 0; + + // You can override these numbers to get a different appearance + protected int mNumWeeks = 6; + protected boolean mShowWeekNumber = false; + protected int mDaysPerWeek = 7; + + // These affect the scroll speed and feel + protected float mFriction = 1.0f; + + protected Context mContext; + protected Handler mHandler; + + protected float mMinimumFlingVelocity; + + // highlighted time + protected Time mSelectedDay = new Time(); + protected SimpleWeeksAdapter mAdapter; + protected ListView mListView; + protected ViewGroup mDayNamesHeader; + protected String[] mDayLabels; + + // disposable variable used for time calculations + protected Time mTempTime = new Time(); + + private static float mScale = 0; + // When the week starts; numbered like Time. (e.g. SUNDAY=0). + protected int mFirstDayOfWeek; + // The first day of the focus month + protected Time mFirstDayOfMonth = new Time(); + // The first day that is visible in the view + protected Time mFirstVisibleDay = new Time(); + // The name of the month to display + protected TextView mMonthName; + // The last name announced by accessibility + protected CharSequence mPrevMonthName; + // which month should be displayed/highlighted [0-11] + protected int mCurrentMonthDisplayed; + // used for tracking during a scroll + protected long mPreviousScrollPosition; + // used for tracking which direction the view is scrolling + protected boolean mIsScrollingUp = false; + // used for tracking what state listview is in + protected int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE; + // used for tracking what state listview is in + protected int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE; + + // This causes an update of the view at midnight + protected Runnable mTodayUpdater = new Runnable() { + @Override + public void run() { + Time midnight = new Time(mFirstVisibleDay.timezone); + midnight.setToNow(); + long currentMillis = midnight.toMillis(true); + + midnight.hour = 0; + midnight.minute = 0; + midnight.second = 0; + midnight.monthDay++; + long millisToMidnight = midnight.normalize(true) - currentMillis; + mHandler.postDelayed(this, millisToMidnight); + + if (mAdapter != null) { + mAdapter.notifyDataSetChanged(); + } + } + }; + + // This allows us to update our position when a day is tapped + protected DataSetObserver mObserver = new DataSetObserver() { + @Override + public void onChanged() { + Time day = mAdapter.getSelectedDay(); + if (day.year != mSelectedDay.year || day.yearDay != mSelectedDay.yearDay) { + goTo(day.toMillis(true), true, true, false); + } + } + }; + + public SimpleDayPickerFragment(long initialTime) { + goTo(initialTime, false, true, true); + mHandler = new Handler(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mContext = activity; + String tz = Time.getCurrentTimezone(); + ViewConfiguration viewConfig = ViewConfiguration.get(activity); + mMinimumFlingVelocity = viewConfig.getScaledMinimumFlingVelocity(); + + // Ensure we're in the correct time zone + mSelectedDay.switchTimezone(tz); + mSelectedDay.normalize(true); + mFirstDayOfMonth.timezone = tz; + mFirstDayOfMonth.normalize(true); + mFirstVisibleDay.timezone = tz; + mFirstVisibleDay.normalize(true); + mTempTime.timezone = tz; + + Resources res = activity.getResources(); + mSaturdayColor = res.getColor(R.color.month_saturday); + mSundayColor = res.getColor(R.color.month_sunday); + mDayNameColor = res.getColor(R.color.month_day_names_color); + + // Adjust sizes for screen density + if (mScale == 0) { + mScale = activity.getResources().getDisplayMetrics().density; + if (mScale != 1) { + WEEK_MIN_VISIBLE_HEIGHT *= mScale; + BOTTOM_BUFFER *= mScale; + LIST_TOP_OFFSET *= mScale; + } + } + setUpAdapter(); + setListAdapter(mAdapter); + } + + /** + * Creates a new adapter if necessary and sets up its parameters. Override + * this method to provide a custom adapter. + */ + protected void setUpAdapter() { + HashMap weekParams = new HashMap(); + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks); + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, mShowWeekNumber ? 1 : 0); + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek); + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, + Time.getJulianDay(mSelectedDay.toMillis(false), mSelectedDay.gmtoff)); + if (mAdapter == null) { + mAdapter = new SimpleWeeksAdapter(getActivity(), weekParams); + mAdapter.registerDataSetObserver(mObserver); + } else { + mAdapter.updateParams(weekParams); + } + // refresh the view with the new parameters + mAdapter.notifyDataSetChanged(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + setUpListView(); + setUpHeader(); + + mMonthName = (TextView) getView().findViewById(R.id.month_name); + SimpleWeekView child = (SimpleWeekView) mListView.getChildAt(0); + if (child == null) { + return; + } + int julianDay = child.getFirstJulianDay(); + mFirstVisibleDay.setJulianDay(julianDay); + // set the title to the month of the second week + mTempTime.setJulianDay(julianDay + DAYS_PER_WEEK); + setMonthDisplayed(mTempTime, true); + } + + /** + * Sets up the strings to be used by the header. Override this method to use + * different strings or modify the view params. + */ + protected void setUpHeader() { + mDayLabels = new String[7]; + for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { + mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString(i, + DateUtils.LENGTH_SHORTEST).toUpperCase(); + } + } + + /** + * Sets all the required fields for the list view. Override this method to + * set a different list view behavior. + */ + protected void setUpListView() { + // Configure the listview + mListView = getListView(); + // Transparent background on scroll + mListView.setCacheColorHint(0); + // No dividers + mListView.setDivider(null); + // Items are clickable + mListView.setItemsCanFocus(true); + // The thumb gets in the way, so disable it + mListView.setFastScrollEnabled(false); + mListView.setVerticalScrollBarEnabled(false); + mListView.setOnScrollListener(this); + mListView.setFadingEdgeLength(0); + // Make the scrolling behavior nicer + mListView.setFriction(ViewConfiguration.getScrollFriction() * mFriction); + } + + @Override + public void onResume() { + super.onResume(); + setUpAdapter(); + doResumeUpdates(); + } + + @Override + public void onPause() { + super.onPause(); + mHandler.removeCallbacks(mTodayUpdater); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putLong(KEY_CURRENT_TIME, mSelectedDay.toMillis(true)); + } + + /** + * Updates the user preference fields. Override this to use a different + * preference space. + */ + protected void doResumeUpdates() { + // Get default week start based on locale, subtracting one for use with android Time. + Calendar cal = Calendar.getInstance(Locale.getDefault()); + mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1; + + mShowWeekNumber = false; + + updateHeader(); + goTo(mSelectedDay.toMillis(true), false, false, false); + mAdapter.setSelectedDay(mSelectedDay); + mTodayUpdater.run(); + } + + /** + * Fixes the day names header to provide correct spacing and updates the + * label text. Override this to set up a custom header. + */ + protected void updateHeader() { + TextView label = (TextView) mDayNamesHeader.findViewById(R.id.wk_label); + if (mShowWeekNumber) { + label.setVisibility(View.VISIBLE); + } else { + label.setVisibility(View.GONE); + } + int offset = mFirstDayOfWeek - 1; + for (int i = 1; i < 8; i++) { + label = (TextView) mDayNamesHeader.getChildAt(i); + if (i < mDaysPerWeek + 1) { + int position = (offset + i) % 7; + label.setText(mDayLabels[position]); + label.setVisibility(View.VISIBLE); + if (position == Time.SATURDAY) { + label.setTextColor(mSaturdayColor); + } else if (position == Time.SUNDAY) { + label.setTextColor(mSundayColor); + } else { + label.setTextColor(mDayNameColor); + } + } else { + label.setVisibility(View.GONE); + } + } + mDayNamesHeader.invalidate(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.month_by_week, + container, false); + mDayNamesHeader = (ViewGroup) v.findViewById(R.id.day_names); + return v; + } + + /** + * Returns the UTC millis since epoch representation of the currently + * selected time. + * + * @return + */ + public long getSelectedTime() { + return mSelectedDay.toMillis(true); + } + + /** + * This moves to the specified time in the view. If the time is not already + * in range it will move the list so that the first of the month containing + * the time is at the top of the view. If the new time is already in view + * the list will not be scrolled unless forceScroll is true. This time may + * optionally be highlighted as selected as well. + * + * @param time The time to move to + * @param animate Whether to scroll to the given time or just redraw at the + * new location + * @param setSelected Whether to set the given time as selected + * @param forceScroll Whether to recenter even if the time is already + * visible + * @return Whether or not the view animated to the new location + */ + public boolean goTo(long time, boolean animate, boolean setSelected, boolean forceScroll) { + if (time == -1) { + Log.e(TAG, "time is invalid"); + return false; + } + + // Set the selected day + if (setSelected) { + mSelectedDay.set(time); + mSelectedDay.normalize(true); + } + + // If this view isn't returned yet we won't be able to load the lists + // current position, so return after setting the selected day. + if (!isResumed()) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "We're not visible yet"); + } + return false; + } + + mTempTime.set(time); + long millis = mTempTime.normalize(true); + // Get the week we're going to + // TODO push Util function into Calendar public api. + int position = Utils.getWeeksSinceEpochFromJulianDay( + Time.getJulianDay(millis, mTempTime.gmtoff), mFirstDayOfWeek); + + View child; + int i = 0; + int top = 0; + // Find a child that's completely in the view + do { + child = mListView.getChildAt(i++); + if (child == null) { + break; + } + top = child.getTop(); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "child at " + (i-1) + " has top " + top); + } + } while (top < 0); + + // Compute the first and last position visible + int firstPosition; + if (child != null) { + firstPosition = mListView.getPositionForView(child); + } else { + firstPosition = 0; + } + int lastPosition = firstPosition + mNumWeeks - 1; + if (top > BOTTOM_BUFFER) { + lastPosition--; + } + + if (setSelected) { + mAdapter.setSelectedDay(mSelectedDay); + } + + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "GoTo position " + position); + } + // Check if the selected day is now outside of our visible range + // and if so scroll to the month that contains it + if (position < firstPosition || position > lastPosition || forceScroll) { + mFirstDayOfMonth.set(mTempTime); + mFirstDayOfMonth.monthDay = 1; + millis = mFirstDayOfMonth.normalize(true); + setMonthDisplayed(mFirstDayOfMonth, true); + position = Utils.getWeeksSinceEpochFromJulianDay( + Time.getJulianDay(millis, mFirstDayOfMonth.gmtoff), mFirstDayOfWeek); + + mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING; + if (animate) { + mListView.smoothScrollToPositionFromTop( + position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION); + return true; + } else { + mListView.setSelectionFromTop(position, LIST_TOP_OFFSET); + // Perform any after scroll operations that are needed + onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE); + } + } else if (setSelected) { + // Otherwise just set the selection + setMonthDisplayed(mSelectedDay, true); + } + return false; + } + + /** + * Updates the title and selected month if the view has moved to a new + * month. + */ + @Override + public void onScroll( + AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + SimpleWeekView child = (SimpleWeekView)view.getChildAt(0); + if (child == null) { + return; + } + + // Figure out where we are + long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom(); + mFirstVisibleDay.setJulianDay(child.getFirstJulianDay()); + + // If we have moved since our last call update the direction + if (currScroll < mPreviousScrollPosition) { + mIsScrollingUp = true; + } else if (currScroll > mPreviousScrollPosition) { + mIsScrollingUp = false; + } else { + return; + } + + mPreviousScrollPosition = currScroll; + mPreviousScrollState = mCurrentScrollState; + + updateMonthHighlight(mListView); + } + + /** + * Figures out if the month being shown has changed and updates the + * highlight if needed + * + * @param view The ListView containing the weeks + */ + private void updateMonthHighlight(AbsListView view) { + SimpleWeekView child = (SimpleWeekView) view.getChildAt(0); + if (child == null) { + return; + } + + // Figure out where we are + int offset = child.getBottom() < WEEK_MIN_VISIBLE_HEIGHT ? 1 : 0; + // Use some hysteresis for checking which month to highlight. This + // causes the month to transition when two full weeks of a month are + // visible. + child = (SimpleWeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset); + + if (child == null) { + return; + } + + // Find out which month we're moving into + int month; + if (mIsScrollingUp) { + month = child.getFirstMonth(); + } else { + month = child.getLastMonth(); + } + + // And how it relates to our current highlighted month + int monthDiff; + if (mCurrentMonthDisplayed == 11 && month == 0) { + monthDiff = 1; + } else if (mCurrentMonthDisplayed == 0 && month == 11) { + monthDiff = -1; + } else { + monthDiff = month - mCurrentMonthDisplayed; + } + + // Only switch months if we're scrolling away from the currently + // selected month + if (monthDiff != 0) { + int julianDay = child.getFirstJulianDay(); + if (mIsScrollingUp) { + // Takes the start of the week + } else { + // Takes the start of the following week + julianDay += DAYS_PER_WEEK; + } + mTempTime.setJulianDay(julianDay); + setMonthDisplayed(mTempTime, false); + } + } + + /** + * Sets the month displayed at the top of this view based on time. Override + * to add custom events when the title is changed. + * + * @param time A day in the new focus month. + * @param updateHighlight TODO(epastern): + */ + protected void setMonthDisplayed(Time time, boolean updateHighlight) { + CharSequence oldMonth = mMonthName.getText(); + mMonthName.setText(Utils.formatMonthYear(mContext, time)); + mMonthName.invalidate(); + if (!TextUtils.equals(oldMonth, mMonthName.getText())) { + mMonthName.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + } + mCurrentMonthDisplayed = time.month; + if (updateHighlight) { + mAdapter.updateFocusMonth(mCurrentMonthDisplayed); + } + } + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + // use a post to prevent re-entering onScrollStateChanged before it + // exits + mScrollStateChangedRunnable.doScrollStateChange(view, scrollState); + } + + protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(); + + protected class ScrollStateRunnable implements Runnable { + private int mNewState; + + /** + * Sets up the runnable with a short delay in case the scroll state + * immediately changes again. + * + * @param view The list view that changed state + * @param scrollState The new state it changed to + */ + public void doScrollStateChange(AbsListView view, int scrollState) { + mHandler.removeCallbacks(this); + mNewState = scrollState; + mHandler.postDelayed(this, SCROLL_CHANGE_DELAY); + } + + public void run() { + mCurrentScrollState = mNewState; + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, + "new scroll state: " + mNewState + " old state: " + mPreviousScrollState); + } + // Fix the position after a scroll or a fling ends + if (mNewState == OnScrollListener.SCROLL_STATE_IDLE + && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) { + mPreviousScrollState = mNewState; + mAdapter.updateFocusMonth(mCurrentMonthDisplayed); + } else { + mPreviousScrollState = mNewState; + } + } + } +} -- cgit v1.2.3 From 34e991005cb84be117a301d30d89c7c00383ad55 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Tue, 29 Jun 2021 00:16:29 +0000 Subject: AOSP/Calendar - Initial conversion of SimpleDayPickerFragment as performed by Android Studio's automatic converter. Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: If7951b07d8871c47bd9cbc1417f5ffcbaf45174d --- .../calendar/month/SimpleDayPickerFragment.kt | 634 ++++++++++----------- 1 file changed, 310 insertions(+), 324 deletions(-) diff --git a/src/com/android/calendar/month/SimpleDayPickerFragment.kt b/src/com/android/calendar/month/SimpleDayPickerFragment.kt index 2efae6a9..f8b38d5c 100644 --- a/src/com/android/calendar/month/SimpleDayPickerFragment.kt +++ b/src/com/android/calendar/month/SimpleDayPickerFragment.kt @@ -13,240 +13,215 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar.month; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.app.Activity; -import android.app.ListFragment; -import android.content.Context; -import android.content.res.Resources; -import android.database.DataSetObserver; -import android.os.Bundle; -import android.os.Handler; -import android.text.TextUtils; -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.ViewConfiguration; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityEvent; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; -import android.widget.ListView; -import android.widget.TextView; - -import java.util.Calendar; -import java.util.HashMap; -import java.util.Locale; +package com.android.calendar.month + +import com.android.calendar.R +import com.android.calendar.Utils +import android.app.Activity +import android.app.ListFragment +import android.content.Context +import android.content.res.Resources +import android.database.DataSetObserver +import android.os.Bundle +import android.os.Handler +import android.text.TextUtils +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.ViewConfiguration +import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent +import android.widget.AbsListView +import android.widget.AbsListView.OnScrollListener +import android.widget.ListView +import android.widget.TextView +import java.util.Calendar +import java.util.HashMap +import java.util.Locale /** - *

+ * + * * This displays a titled list of weeks with selectable days. It can be * configured to display the week number, start the week on a given day, show a * reduced number of days, or display an arbitrary number of weeks at a time. By * overriding methods and changing variables this fragment can be customized to * easily display a month selection component in a given style. - *

+ * */ -public class SimpleDayPickerFragment extends ListFragment implements OnScrollListener { - - private static final String TAG = "MonthFragment"; - private static final String KEY_CURRENT_TIME = "current_time"; - - // Affects when the month selection will change while scrolling up - protected static final int SCROLL_HYST_WEEKS = 2; - // How long the GoTo fling animation should last - protected static final int GOTO_SCROLL_DURATION = 500; - // How long to wait after receiving an onScrollStateChanged notification - // before acting on it - protected static final int SCROLL_CHANGE_DELAY = 40; - // The number of days to display in each week - public static final int DAYS_PER_WEEK = 7; - // The size of the month name displayed above the week list - protected static final int MINI_MONTH_NAME_TEXT_SIZE = 18; - public static int LIST_TOP_OFFSET = -1; // so that the top line will be under the separator - protected int WEEK_MIN_VISIBLE_HEIGHT = 12; - protected int BOTTOM_BUFFER = 20; - protected int mSaturdayColor = 0; - protected int mSundayColor = 0; - protected int mDayNameColor = 0; +class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListener { + protected var WEEK_MIN_VISIBLE_HEIGHT = 12 + protected var BOTTOM_BUFFER = 20 + protected var mSaturdayColor = 0 + protected var mSundayColor = 0 + protected var mDayNameColor = 0 // You can override these numbers to get a different appearance - protected int mNumWeeks = 6; - protected boolean mShowWeekNumber = false; - protected int mDaysPerWeek = 7; + protected var mNumWeeks = 6 + protected var mShowWeekNumber = false + protected var mDaysPerWeek = 7 // These affect the scroll speed and feel - protected float mFriction = 1.0f; - - protected Context mContext; - protected Handler mHandler; - - protected float mMinimumFlingVelocity; + protected var mFriction = 1.0f + protected var mContext: Context? = null + protected var mHandler: Handler + protected var mMinimumFlingVelocity = 0f // highlighted time - protected Time mSelectedDay = new Time(); - protected SimpleWeeksAdapter mAdapter; - protected ListView mListView; - protected ViewGroup mDayNamesHeader; - protected String[] mDayLabels; + protected var mSelectedDay: Time = Time() + protected var mAdapter: SimpleWeeksAdapter? = null + protected var mListView: ListView? = null + protected var mDayNamesHeader: ViewGroup? = null + protected var mDayLabels: Array // disposable variable used for time calculations - protected Time mTempTime = new Time(); + protected var mTempTime: Time = Time() - private static float mScale = 0; // When the week starts; numbered like Time. (e.g. SUNDAY=0). - protected int mFirstDayOfWeek; + protected var mFirstDayOfWeek = 0 + // The first day of the focus month - protected Time mFirstDayOfMonth = new Time(); + protected var mFirstDayOfMonth: Time = Time() + // The first day that is visible in the view - protected Time mFirstVisibleDay = new Time(); + protected var mFirstVisibleDay: Time = Time() + // The name of the month to display - protected TextView mMonthName; + protected var mMonthName: TextView? = null + // The last name announced by accessibility - protected CharSequence mPrevMonthName; + protected var mPrevMonthName: CharSequence? = null + // which month should be displayed/highlighted [0-11] - protected int mCurrentMonthDisplayed; + protected var mCurrentMonthDisplayed = 0 + // used for tracking during a scroll - protected long mPreviousScrollPosition; + protected var mPreviousScrollPosition: Long = 0 + // used for tracking which direction the view is scrolling - protected boolean mIsScrollingUp = false; + protected var mIsScrollingUp = false + // used for tracking what state listview is in - protected int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE; + protected var mPreviousScrollState: Int = OnScrollListener.SCROLL_STATE_IDLE + // used for tracking what state listview is in - protected int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE; + protected var mCurrentScrollState: Int = OnScrollListener.SCROLL_STATE_IDLE // This causes an update of the view at midnight - protected Runnable mTodayUpdater = new Runnable() { + protected var mTodayUpdater: Runnable = object : Runnable() { @Override - public void run() { - Time midnight = new Time(mFirstVisibleDay.timezone); - midnight.setToNow(); - long currentMillis = midnight.toMillis(true); - - midnight.hour = 0; - midnight.minute = 0; - midnight.second = 0; - midnight.monthDay++; - long millisToMidnight = midnight.normalize(true) - currentMillis; - mHandler.postDelayed(this, millisToMidnight); - + fun run() { + val midnight = Time(mFirstVisibleDay.timezone) + midnight.setToNow() + val currentMillis: Long = midnight.toMillis(true) + midnight.hour = 0 + midnight.minute = 0 + midnight.second = 0 + midnight.monthDay++ + val millisToMidnight: Long = midnight.normalize(true) - currentMillis + mHandler.postDelayed(this, millisToMidnight) if (mAdapter != null) { - mAdapter.notifyDataSetChanged(); + mAdapter.notifyDataSetChanged() } } - }; + } // This allows us to update our position when a day is tapped - protected DataSetObserver mObserver = new DataSetObserver() { + protected var mObserver: DataSetObserver = object : DataSetObserver() { @Override - public void onChanged() { - Time day = mAdapter.getSelectedDay(); - if (day.year != mSelectedDay.year || day.yearDay != mSelectedDay.yearDay) { - goTo(day.toMillis(true), true, true, false); + fun onChanged() { + val day: Time = mAdapter.getSelectedDay() + if (day.year !== mSelectedDay.year || day.yearDay !== mSelectedDay.yearDay) { + goTo(day.toMillis(true), true, true, false) } } - }; - - public SimpleDayPickerFragment(long initialTime) { - goTo(initialTime, false, true, true); - mHandler = new Handler(); } @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mContext = activity; - String tz = Time.getCurrentTimezone(); - ViewConfiguration viewConfig = ViewConfiguration.get(activity); - mMinimumFlingVelocity = viewConfig.getScaledMinimumFlingVelocity(); + fun onAttach(activity: Activity) { + super.onAttach(activity) + mContext = activity + val tz: String = Time.getCurrentTimezone() + val viewConfig: ViewConfiguration = ViewConfiguration.get(activity) + mMinimumFlingVelocity = viewConfig.getScaledMinimumFlingVelocity() // Ensure we're in the correct time zone - mSelectedDay.switchTimezone(tz); - mSelectedDay.normalize(true); - mFirstDayOfMonth.timezone = tz; - mFirstDayOfMonth.normalize(true); - mFirstVisibleDay.timezone = tz; - mFirstVisibleDay.normalize(true); - mTempTime.timezone = tz; - - Resources res = activity.getResources(); - mSaturdayColor = res.getColor(R.color.month_saturday); - mSundayColor = res.getColor(R.color.month_sunday); - mDayNameColor = res.getColor(R.color.month_day_names_color); + mSelectedDay.switchTimezone(tz) + mSelectedDay.normalize(true) + mFirstDayOfMonth.timezone = tz + mFirstDayOfMonth.normalize(true) + mFirstVisibleDay.timezone = tz + mFirstVisibleDay.normalize(true) + mTempTime.timezone = tz + val res: Resources = activity.getResources() + mSaturdayColor = res.getColor(R.color.month_saturday) + mSundayColor = res.getColor(R.color.month_sunday) + mDayNameColor = res.getColor(R.color.month_day_names_color) // Adjust sizes for screen density - if (mScale == 0) { - mScale = activity.getResources().getDisplayMetrics().density; - if (mScale != 1) { - WEEK_MIN_VISIBLE_HEIGHT *= mScale; - BOTTOM_BUFFER *= mScale; - LIST_TOP_OFFSET *= mScale; + if (mScale == 0f) { + mScale = activity.getResources().getDisplayMetrics().density + if (mScale != 1f) { + WEEK_MIN_VISIBLE_HEIGHT *= mScale.toInt() + BOTTOM_BUFFER *= mScale.toInt() + LIST_TOP_OFFSET *= mScale.toInt() } } - setUpAdapter(); - setListAdapter(mAdapter); + setUpAdapter() + setListAdapter(mAdapter) } /** * Creates a new adapter if necessary and sets up its parameters. Override * this method to provide a custom adapter. */ - protected void setUpAdapter() { - HashMap weekParams = new HashMap(); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, mShowWeekNumber ? 1 : 0); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek); + protected fun setUpAdapter() { + val weekParams: HashMap = HashMap() + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks) + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, if (mShowWeekNumber) 1 else 0) + weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek) weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, - Time.getJulianDay(mSelectedDay.toMillis(false), mSelectedDay.gmtoff)); + Time.getJulianDay(mSelectedDay.toMillis(false), mSelectedDay.gmtoff)) if (mAdapter == null) { - mAdapter = new SimpleWeeksAdapter(getActivity(), weekParams); - mAdapter.registerDataSetObserver(mObserver); + mAdapter = SimpleWeeksAdapter(getActivity(), weekParams) + mAdapter.registerDataSetObserver(mObserver) } else { - mAdapter.updateParams(weekParams); + mAdapter.updateParams(weekParams) } // refresh the view with the new parameters - mAdapter.notifyDataSetChanged(); + mAdapter.notifyDataSetChanged() } @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - setUpListView(); - setUpHeader(); - - mMonthName = (TextView) getView().findViewById(R.id.month_name); - SimpleWeekView child = (SimpleWeekView) mListView.getChildAt(0); - if (child == null) { - return; - } - int julianDay = child.getFirstJulianDay(); - mFirstVisibleDay.setJulianDay(julianDay); + fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + setUpListView() + setUpHeader() + mMonthName = getView().findViewById(R.id.month_name) as TextView + val child: SimpleWeekView = mListView.getChildAt(0) as SimpleWeekView ?: return + val julianDay: Int = child.getFirstJulianDay() + mFirstVisibleDay.setJulianDay(julianDay) // set the title to the month of the second week - mTempTime.setJulianDay(julianDay + DAYS_PER_WEEK); - setMonthDisplayed(mTempTime, true); + mTempTime.setJulianDay(julianDay + DAYS_PER_WEEK) + setMonthDisplayed(mTempTime, true) } /** * Sets up the strings to be used by the header. Override this method to use * different strings or modify the view params. */ - protected void setUpHeader() { - mDayLabels = new String[7]; - for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { + protected fun setUpHeader() { + mDayLabels = arrayOfNulls(7) + for (i in Calendar.SUNDAY..Calendar.SATURDAY) { mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString(i, - DateUtils.LENGTH_SHORTEST).toUpperCase(); + DateUtils.LENGTH_SHORTEST).toUpperCase() } } @@ -254,97 +229,95 @@ public class SimpleDayPickerFragment extends ListFragment implements OnScrollLis * Sets all the required fields for the list view. Override this method to * set a different list view behavior. */ - protected void setUpListView() { + protected fun setUpListView() { // Configure the listview - mListView = getListView(); + mListView = getListView() // Transparent background on scroll - mListView.setCacheColorHint(0); + mListView.setCacheColorHint(0) // No dividers - mListView.setDivider(null); + mListView.setDivider(null) // Items are clickable - mListView.setItemsCanFocus(true); + mListView.setItemsCanFocus(true) // The thumb gets in the way, so disable it - mListView.setFastScrollEnabled(false); - mListView.setVerticalScrollBarEnabled(false); - mListView.setOnScrollListener(this); - mListView.setFadingEdgeLength(0); + mListView.setFastScrollEnabled(false) + mListView.setVerticalScrollBarEnabled(false) + mListView.setOnScrollListener(this) + mListView.setFadingEdgeLength(0) // Make the scrolling behavior nicer - mListView.setFriction(ViewConfiguration.getScrollFriction() * mFriction); + mListView.setFriction(ViewConfiguration.getScrollFriction() * mFriction) } @Override - public void onResume() { - super.onResume(); - setUpAdapter(); - doResumeUpdates(); + fun onResume() { + super.onResume() + setUpAdapter() + doResumeUpdates() } @Override - public void onPause() { - super.onPause(); - mHandler.removeCallbacks(mTodayUpdater); + fun onPause() { + super.onPause() + mHandler.removeCallbacks(mTodayUpdater) } @Override - public void onSaveInstanceState(Bundle outState) { - outState.putLong(KEY_CURRENT_TIME, mSelectedDay.toMillis(true)); + fun onSaveInstanceState(outState: Bundle) { + outState.putLong(KEY_CURRENT_TIME, mSelectedDay.toMillis(true)) } /** * Updates the user preference fields. Override this to use a different * preference space. */ - protected void doResumeUpdates() { + protected fun doResumeUpdates() { // Get default week start based on locale, subtracting one for use with android Time. - Calendar cal = Calendar.getInstance(Locale.getDefault()); - mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1; - - mShowWeekNumber = false; - - updateHeader(); - goTo(mSelectedDay.toMillis(true), false, false, false); - mAdapter.setSelectedDay(mSelectedDay); - mTodayUpdater.run(); + val cal: Calendar = Calendar.getInstance(Locale.getDefault()) + mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1 + mShowWeekNumber = false + updateHeader() + goTo(mSelectedDay.toMillis(true), false, false, false) + mAdapter.setSelectedDay(mSelectedDay) + mTodayUpdater.run() } /** * Fixes the day names header to provide correct spacing and updates the * label text. Override this to set up a custom header. */ - protected void updateHeader() { - TextView label = (TextView) mDayNamesHeader.findViewById(R.id.wk_label); + protected fun updateHeader() { + var label: TextView = mDayNamesHeader.findViewById(R.id.wk_label) as TextView if (mShowWeekNumber) { - label.setVisibility(View.VISIBLE); + label.setVisibility(View.VISIBLE) } else { - label.setVisibility(View.GONE); + label.setVisibility(View.GONE) } - int offset = mFirstDayOfWeek - 1; - for (int i = 1; i < 8; i++) { - label = (TextView) mDayNamesHeader.getChildAt(i); + val offset = mFirstDayOfWeek - 1 + for (i in 1..7) { + label = mDayNamesHeader.getChildAt(i) as TextView if (i < mDaysPerWeek + 1) { - int position = (offset + i) % 7; - label.setText(mDayLabels[position]); - label.setVisibility(View.VISIBLE); + val position = (offset + i) % 7 + label.setText(mDayLabels[position]) + label.setVisibility(View.VISIBLE) if (position == Time.SATURDAY) { - label.setTextColor(mSaturdayColor); + label.setTextColor(mSaturdayColor) } else if (position == Time.SUNDAY) { - label.setTextColor(mSundayColor); + label.setTextColor(mSundayColor) } else { - label.setTextColor(mDayNameColor); + label.setTextColor(mDayNameColor) } } else { - label.setVisibility(View.GONE); + label.setVisibility(View.GONE) } } - mDayNamesHeader.invalidate(); + mDayNamesHeader.invalidate() } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.month_by_week, - container, false); - mDayNamesHeader = (ViewGroup) v.findViewById(R.id.day_names); - return v; + fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + val v: View = inflater.inflate(R.layout.month_by_week, + container, false) + mDayNamesHeader = v.findViewById(R.id.day_names) as ViewGroup + return v } /** @@ -353,9 +326,8 @@ public class SimpleDayPickerFragment extends ListFragment implements OnScrollLis * * @return */ - public long getSelectedTime() { - return mSelectedDay.toMillis(true); - } + val selectedTime: Long + get() = mSelectedDay.toMillis(true) /** * This moves to the specified time in the view. If the time is not already @@ -366,130 +338,120 @@ public class SimpleDayPickerFragment extends ListFragment implements OnScrollLis * * @param time The time to move to * @param animate Whether to scroll to the given time or just redraw at the - * new location + * new location * @param setSelected Whether to set the given time as selected * @param forceScroll Whether to recenter even if the time is already - * visible + * visible * @return Whether or not the view animated to the new location */ - public boolean goTo(long time, boolean animate, boolean setSelected, boolean forceScroll) { - if (time == -1) { - Log.e(TAG, "time is invalid"); - return false; + fun goTo(time: Long, animate: Boolean, setSelected: Boolean, forceScroll: Boolean): Boolean { + if (time == -1L) { + Log.e(TAG, "time is invalid") + return false } // Set the selected day if (setSelected) { - mSelectedDay.set(time); - mSelectedDay.normalize(true); + mSelectedDay.set(time) + mSelectedDay.normalize(true) } // If this view isn't returned yet we won't be able to load the lists // current position, so return after setting the selected day. if (!isResumed()) { if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "We're not visible yet"); + Log.d(TAG, "We're not visible yet") } - return false; + return false } - - mTempTime.set(time); - long millis = mTempTime.normalize(true); + mTempTime.set(time) + var millis: Long = mTempTime.normalize(true) // Get the week we're going to // TODO push Util function into Calendar public api. - int position = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mTempTime.gmtoff), mFirstDayOfWeek); - - View child; - int i = 0; - int top = 0; + var position: Int = Utils.getWeeksSinceEpochFromJulianDay( + Time.getJulianDay(millis, mTempTime.gmtoff), mFirstDayOfWeek) + var child: View + var i = 0 + var top = 0 // Find a child that's completely in the view do { - child = mListView.getChildAt(i++); + child = mListView.getChildAt(i++) if (child == null) { - break; + break } - top = child.getTop(); + top = child.getTop() if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "child at " + (i-1) + " has top " + top); + Log.d(TAG, "child at " + (i - 1) + " has top " + top) } - } while (top < 0); + } while (top < 0) // Compute the first and last position visible - int firstPosition; - if (child != null) { - firstPosition = mListView.getPositionForView(child); + val firstPosition: Int + firstPosition = if (child != null) { + mListView.getPositionForView(child) } else { - firstPosition = 0; + 0 } - int lastPosition = firstPosition + mNumWeeks - 1; + var lastPosition = firstPosition + mNumWeeks - 1 if (top > BOTTOM_BUFFER) { - lastPosition--; + lastPosition-- } - if (setSelected) { - mAdapter.setSelectedDay(mSelectedDay); + mAdapter.setSelectedDay(mSelectedDay) } - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "GoTo position " + position); + Log.d(TAG, "GoTo position $position") } // Check if the selected day is now outside of our visible range // and if so scroll to the month that contains it if (position < firstPosition || position > lastPosition || forceScroll) { - mFirstDayOfMonth.set(mTempTime); - mFirstDayOfMonth.monthDay = 1; - millis = mFirstDayOfMonth.normalize(true); - setMonthDisplayed(mFirstDayOfMonth, true); + mFirstDayOfMonth.set(mTempTime) + mFirstDayOfMonth.monthDay = 1 + millis = mFirstDayOfMonth.normalize(true) + setMonthDisplayed(mFirstDayOfMonth, true) position = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mFirstDayOfMonth.gmtoff), mFirstDayOfWeek); - - mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING; + Time.getJulianDay(millis, mFirstDayOfMonth.gmtoff), mFirstDayOfWeek) + mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING if (animate) { mListView.smoothScrollToPositionFromTop( - position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION); - return true; + position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION) + return true } else { - mListView.setSelectionFromTop(position, LIST_TOP_OFFSET); + mListView.setSelectionFromTop(position, LIST_TOP_OFFSET) // Perform any after scroll operations that are needed - onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE); + onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE) } } else if (setSelected) { // Otherwise just set the selection - setMonthDisplayed(mSelectedDay, true); + setMonthDisplayed(mSelectedDay, true) } - return false; + return false } - /** + /** * Updates the title and selected month if the view has moved to a new * month. */ @Override - public void onScroll( - AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - SimpleWeekView child = (SimpleWeekView)view.getChildAt(0); - if (child == null) { - return; - } + fun onScroll( + view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) { + val child: SimpleWeekView = view.getChildAt(0) as SimpleWeekView ?: return // Figure out where we are - long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom(); - mFirstVisibleDay.setJulianDay(child.getFirstJulianDay()); + val currScroll: Long = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom() + mFirstVisibleDay.setJulianDay(child.getFirstJulianDay()) // If we have moved since our last call update the direction - if (currScroll < mPreviousScrollPosition) { - mIsScrollingUp = true; + mIsScrollingUp = if (currScroll < mPreviousScrollPosition) { + true } else if (currScroll > mPreviousScrollPosition) { - mIsScrollingUp = false; + false } else { - return; + return } - - mPreviousScrollPosition = currScroll; - mPreviousScrollState = mCurrentScrollState; - - updateMonthHighlight(mListView); + mPreviousScrollPosition = currScroll + mPreviousScrollState = mCurrentScrollState + updateMonthHighlight(mListView) } /** @@ -498,53 +460,49 @@ public class SimpleDayPickerFragment extends ListFragment implements OnScrollLis * * @param view The ListView containing the weeks */ - private void updateMonthHighlight(AbsListView view) { - SimpleWeekView child = (SimpleWeekView) view.getChildAt(0); - if (child == null) { - return; - } + private fun updateMonthHighlight(view: AbsListView) { + var child: SimpleWeekView = view.getChildAt(0) as SimpleWeekView ?: return // Figure out where we are - int offset = child.getBottom() < WEEK_MIN_VISIBLE_HEIGHT ? 1 : 0; + val offset = if (child.getBottom() < WEEK_MIN_VISIBLE_HEIGHT) 1 else 0 // Use some hysteresis for checking which month to highlight. This // causes the month to transition when two full weeks of a month are // visible. - child = (SimpleWeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset); - + child = view.getChildAt(SCROLL_HYST_WEEKS + offset) as SimpleWeekView if (child == null) { - return; + return } // Find out which month we're moving into - int month; - if (mIsScrollingUp) { - month = child.getFirstMonth(); + val month: Int + month = if (mIsScrollingUp) { + child.getFirstMonth() } else { - month = child.getLastMonth(); + child.getLastMonth() } // And how it relates to our current highlighted month - int monthDiff; - if (mCurrentMonthDisplayed == 11 && month == 0) { - monthDiff = 1; + val monthDiff: Int + monthDiff = if (mCurrentMonthDisplayed == 11 && month == 0) { + 1 } else if (mCurrentMonthDisplayed == 0 && month == 11) { - monthDiff = -1; + -1 } else { - monthDiff = month - mCurrentMonthDisplayed; + month - mCurrentMonthDisplayed } // Only switch months if we're scrolling away from the currently // selected month if (monthDiff != 0) { - int julianDay = child.getFirstJulianDay(); + var julianDay: Int = child.getFirstJulianDay() if (mIsScrollingUp) { // Takes the start of the week } else { // Takes the start of the following week - julianDay += DAYS_PER_WEEK; + julianDay += DAYS_PER_WEEK } - mTempTime.setJulianDay(julianDay); - setMonthDisplayed(mTempTime, false); + mTempTime.setJulianDay(julianDay) + setMonthDisplayed(mTempTime, false) } } @@ -555,30 +513,30 @@ public class SimpleDayPickerFragment extends ListFragment implements OnScrollLis * @param time A day in the new focus month. * @param updateHighlight TODO(epastern): */ - protected void setMonthDisplayed(Time time, boolean updateHighlight) { - CharSequence oldMonth = mMonthName.getText(); - mMonthName.setText(Utils.formatMonthYear(mContext, time)); - mMonthName.invalidate(); + protected fun setMonthDisplayed(time: Time, updateHighlight: Boolean) { + val oldMonth: CharSequence = mMonthName.getText() + mMonthName.setText(Utils.formatMonthYear(mContext, time)) + mMonthName.invalidate() if (!TextUtils.equals(oldMonth, mMonthName.getText())) { - mMonthName.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + mMonthName.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } - mCurrentMonthDisplayed = time.month; + mCurrentMonthDisplayed = time.month if (updateHighlight) { - mAdapter.updateFocusMonth(mCurrentMonthDisplayed); + mAdapter.updateFocusMonth(mCurrentMonthDisplayed) } } @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { + fun onScrollStateChanged(view: AbsListView?, scrollState: Int) { // use a post to prevent re-entering onScrollStateChanged before it // exits - mScrollStateChangedRunnable.doScrollStateChange(view, scrollState); + mScrollStateChangedRunnable.doScrollStateChange(view, scrollState) } - protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(); + protected var mScrollStateChangedRunnable: ScrollStateRunnable = ScrollStateRunnable() - protected class ScrollStateRunnable implements Runnable { - private int mNewState; + protected inner class ScrollStateRunnable : Runnable { + private var mNewState = 0 /** * Sets up the runnable with a short delay in case the scroll state @@ -587,26 +545,54 @@ public class SimpleDayPickerFragment extends ListFragment implements OnScrollLis * @param view The list view that changed state * @param scrollState The new state it changed to */ - public void doScrollStateChange(AbsListView view, int scrollState) { - mHandler.removeCallbacks(this); - mNewState = scrollState; - mHandler.postDelayed(this, SCROLL_CHANGE_DELAY); + fun doScrollStateChange(view: AbsListView?, scrollState: Int) { + mHandler.removeCallbacks(this) + mNewState = scrollState + mHandler.postDelayed(this, SCROLL_CHANGE_DELAY) } - public void run() { - mCurrentScrollState = mNewState; + fun run() { + mCurrentScrollState = mNewState if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, - "new scroll state: " + mNewState + " old state: " + mPreviousScrollState); + "new scroll state: $mNewState old state: $mPreviousScrollState") } // Fix the position after a scroll or a fling ends if (mNewState == OnScrollListener.SCROLL_STATE_IDLE && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) { - mPreviousScrollState = mNewState; - mAdapter.updateFocusMonth(mCurrentMonthDisplayed); + mPreviousScrollState = mNewState + mAdapter.updateFocusMonth(mCurrentMonthDisplayed) } else { - mPreviousScrollState = mNewState; + mPreviousScrollState = mNewState } } } -} + + companion object { + private const val TAG = "MonthFragment" + private const val KEY_CURRENT_TIME = "current_time" + + // Affects when the month selection will change while scrolling up + protected const val SCROLL_HYST_WEEKS = 2 + + // How long the GoTo fling animation should last + protected const val GOTO_SCROLL_DURATION = 500 + + // How long to wait after receiving an onScrollStateChanged notification + // before acting on it + protected const val SCROLL_CHANGE_DELAY = 40 + + // The number of days to display in each week + const val DAYS_PER_WEEK = 7 + + // The size of the month name displayed above the week list + protected const val MINI_MONTH_NAME_TEXT_SIZE = 18 + var LIST_TOP_OFFSET = -1 // so that the top line will be under the separator + private var mScale = 0f + } + + init { + goTo(initialTime, false, true, true) + mHandler = Handler() + } +} \ No newline at end of file -- cgit v1.2.3 From 35942094c5f8ab387a95919ae7529ece3eff107a Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Tue, 29 Jun 2021 19:05:15 +0000 Subject: AOSP/Calendar - Given the changes to SimpleDayPickerFragment.kt, these 3 other files had to be modified to fix build errors Test: none, must be merged with SimpleDayPickerFragment.kt changes to build and run tests Change-Id: I69fe87fdf4fceb8c459a2ec840c3f5feca4896aa --- src/com/android/calendar/CalendarController.kt | 192 ++++++++++----------- src/com/android/calendar/EventInfoActivity.kt | 26 +-- .../android/calendar/month/MonthByWeekFragment.kt | 118 ++++++------- 3 files changed, 168 insertions(+), 168 deletions(-) diff --git a/src/com/android/calendar/CalendarController.kt b/src/com/android/calendar/CalendarController.kt index b5484ffc..4d41d3b7 100644 --- a/src/com/android/calendar/CalendarController.kt +++ b/src/com/android/calendar/CalendarController.kt @@ -33,17 +33,17 @@ import java.util.LinkedHashMap import java.util.LinkedList import java.util.WeakHashMap -class CalendarController private constructor(context: Context) { +class CalendarController private constructor(context: Context?) { private var mContext: Context? = null // This uses a LinkedHashMap so that we can replace fragments based on the // view id they are being expanded into since we can't guarantee a reference // to the handler will be findable private val eventHandlers: LinkedHashMap = - LinkedHashMap(5) + LinkedHashMap(5) private val mToBeRemovedEventHandlers: LinkedList = LinkedList() private val mToBeAddedEventHandlers: LinkedHashMap = - LinkedHashMap() + LinkedHashMap() private var mFirstEventHandler: Pair? = null private var mToBeAddedFirstEventHandler: Pair? = null @@ -185,17 +185,17 @@ class CalendarController private constructor(context: Context) { var extra = if (allDay) ALL_DAY_MASK else 0 extra = when (response) { Attendees.ATTENDEE_STATUS_NONE -> extra or - ATTENDEE_STATUS_NONE_MASK.toLong() + ATTENDEE_STATUS_NONE_MASK.toLong() Attendees.ATTENDEE_STATUS_ACCEPTED -> extra or - ATTENDEE_STATUS_ACCEPTED_MASK.toLong() + ATTENDEE_STATUS_ACCEPTED_MASK.toLong() Attendees.ATTENDEE_STATUS_DECLINED -> extra or - ATTENDEE_STATUS_DECLINED_MASK.toLong() + ATTENDEE_STATUS_DECLINED_MASK.toLong() Attendees.ATTENDEE_STATUS_TENTATIVE -> extra or - ATTENDEE_STATUS_TENTATIVE_MASK.toLong() + ATTENDEE_STATUS_TENTATIVE_MASK.toLong() else -> { Log.wtf( - TAG, - "Unknown attendee response $response" + TAG, + "Unknown attendee response $response" ) extra or ATTENDEE_STATUS_NONE_MASK.toLong() } @@ -217,23 +217,23 @@ class CalendarController private constructor(context: Context) { } fun sendEventRelatedEvent( - sender: Object?, - eventType: Long, - eventId: Long, - startMillis: Long, - endMillis: Long, - x: Int, - y: Int, - selectedMillis: Long + sender: Object?, + eventType: Long, + eventId: Long, + startMillis: Long, + endMillis: Long, + x: Int, + y: Int, + selectedMillis: Long ) { // TODO: pass the real allDay status or at least a status that says we don't know the // status and have the receiver query the data. // The current use of this method for VIEW_EVENT is by the day view to show an EventInfo // so currently the missing allDay status has no effect. sendEventRelatedEventWithExtra( - sender, eventType, eventId, startMillis, endMillis, x, y, - EventInfo.buildViewExtraLong(Attendees.ATTENDEE_STATUS_NONE, false), - selectedMillis + sender, eventType, eventId, startMillis, endMillis, x, y, + EventInfo.buildViewExtraLong(Attendees.ATTENDEE_STATUS_NONE, false), + selectedMillis ) } @@ -252,19 +252,19 @@ class CalendarController private constructor(context: Context) { * @param selectedMillis The time to specify as selected */ fun sendEventRelatedEventWithExtra( - sender: Object?, - eventType: Long, - eventId: Long, - startMillis: Long, - endMillis: Long, - x: Int, - y: Int, - extraLong: Long, - selectedMillis: Long + sender: Object?, + eventType: Long, + eventId: Long, + startMillis: Long, + endMillis: Long, + x: Int, + y: Int, + extraLong: Long, + selectedMillis: Long ) { sendEventRelatedEventWithExtraWithTitleWithCalendarId( - sender, eventType, eventId, - startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1 + sender, eventType, eventId, + startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1 ) } @@ -285,17 +285,17 @@ class CalendarController private constructor(context: Context) { * @param calendarId The id of the calendar which the event belongs to */ fun sendEventRelatedEventWithExtraWithTitleWithCalendarId( - sender: Object?, - eventType: Long, - eventId: Long, - startMillis: Long, - endMillis: Long, - x: Int, - y: Int, - extraLong: Long, - selectedMillis: Long, - title: String?, - calendarId: Long + sender: Object?, + eventType: Long, + eventId: Long, + startMillis: Long, + endMillis: Long, + x: Int, + y: Int, + extraLong: Long, + selectedMillis: Long, + title: String?, + calendarId: Long ) { val info = EventInfo() info.eventType = eventType @@ -332,16 +332,16 @@ class CalendarController private constructor(context: Context) { * @param viewType [ViewType] */ fun sendEvent( - sender: Object?, - eventType: Long, - start: Time?, - end: Time?, - eventId: Long, - viewType: Int + sender: Object?, + eventType: Long, + start: Time?, + end: Time?, + eventId: Long, + viewType: Int ) { sendEvent( - sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null, - null + sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null, + null ) } @@ -349,33 +349,33 @@ class CalendarController private constructor(context: Context) { * sendEvent() variant with extraLong, search query, and search component name. */ fun sendEvent( - sender: Object?, - eventType: Long, - start: Time?, - end: Time?, - eventId: Long, - viewType: Int, - extraLong: Long, - query: String?, - componentName: ComponentName? + sender: Object?, + eventType: Long, + start: Time?, + end: Time?, + eventId: Long, + viewType: Int, + extraLong: Long, + query: String?, + componentName: ComponentName? ) { sendEvent( - sender, eventType, start, end, start, eventId, viewType, extraLong, query, - componentName + sender, eventType, start, end, start, eventId, viewType, extraLong, query, + componentName ) } fun sendEvent( - sender: Object?, - eventType: Long, - start: Time?, - end: Time?, - selected: Time?, - eventId: Long, - viewType: Int, - extraLong: Long, - query: String?, - componentName: ComponentName? + sender: Object?, + eventType: Long, + start: Time?, + end: Time?, + selected: Time?, + eventId: Long, + viewType: Int, + extraLong: Long, + query: String?, + componentName: ComponentName? ) { val info = EventInfo() info.eventType = eventType @@ -414,21 +414,21 @@ class CalendarController private constructor(context: Context) { } else if (event.viewType != ViewType.EDIT) { viewType = event.viewType if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY || - Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK) { + Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK) { mDetailViewType = viewType } } if (DEBUG) { Log.d(TAG, "vvvvvvvvvvvvvvv") Log.d( - TAG, - "Start " + if (event.startTime == null) "null" else event.startTime.toString() + TAG, + "Start " + if (event.startTime == null) "null" else event.startTime.toString() ) Log.d(TAG, "End " + if (event.endTime == null) "null" else event.endTime.toString()) Log.d( - TAG, - "Select " + if (event.selectedTime == null) "null" - else event.selectedTime.toString() + TAG, + "Select " + if (event.selectedTime == null) "null" + else event.selectedTime.toString() ) Log.d(TAG, "mTime " + if (mTime == null) "null" else mTime.toString()) } @@ -449,7 +449,7 @@ class CalendarController private constructor(context: Context) { val mtimeMillis: Long = mTime?.toMillis(false) as Long val temp2 = event.endTime if (mtimeMillis < startMillis || - temp2 != null && mtimeMillis > temp2.toMillis(false)) { + temp2 != null && mtimeMillis > temp2.toMillis(false)) { mTime?.set(event.startTime) } } @@ -466,16 +466,16 @@ class CalendarController private constructor(context: Context) { } if (DEBUG) { Log.d( - TAG, - "Start " + if (event.startTime == null) "null" else - event.startTime.toString() + TAG, + "Start " + if (event.startTime == null) "null" else + event.startTime.toString() ) Log.d(TAG, "End " + if (event.endTime == null) "null" else event.endTime.toString()) Log.d( - TAG, - "Select " + if (event.selectedTime == null) "null" else - event.selectedTime.toString() + TAG, + "Select " + if (event.selectedTime == null) "null" else + event.selectedTime.toString() ) Log.d(TAG, "mTime " + if (mTime == null) "null" else mTime.toString()) Log.d(TAG, "^^^^^^^^^^^^^^^") @@ -494,8 +494,8 @@ class CalendarController private constructor(context: Context) { mDispatchInProgressCounter++ if (DEBUG) { Log.d( - TAG, - "sendEvent: Dispatching to " + eventHandlers.size.toString() + " handlers" + TAG, + "sendEvent: Dispatching to " + eventHandlers.size.toString() + " handlers" ) } // Dispatch to event handler(s) @@ -504,16 +504,16 @@ class CalendarController private constructor(context: Context) { // Handle the 'first' one before handling the others val handler: EventHandler? = mFirstEventHandler?.second if (handler != null && handler.supportedEventTypes and event.eventType != 0L && - !mToBeRemovedEventHandlers.contains(mFirstEventHandler?.first)) { + !mToBeRemovedEventHandlers.contains(mFirstEventHandler?.first)) { handler.handleEvent(event) handled = true } } val handlers: MutableIterator> = eventHandlers.entries.iterator() + CalendarController.EventHandler>> = eventHandlers.entries.iterator() while (handlers.hasNext()) { val entry: MutableMap.MutableEntry = handlers.next() + CalendarController.EventHandler> = handlers.next() val key: Int = entry.key.toInt() val temp4 = mFirstEventHandler if (temp4 != null && key.toInt() == temp4.first.toInt()) { @@ -522,7 +522,7 @@ class CalendarController private constructor(context: Context) { } val eventHandler: EventHandler = entry.value if (eventHandler != null && - eventHandler.supportedEventTypes and event.eventType != 0L) { + eventHandler.supportedEventTypes and event.eventType != 0L) { if (mToBeRemovedEventHandlers.contains(key as Integer)) { continue } @@ -569,7 +569,7 @@ class CalendarController private constructor(context: Context) { synchronized(this) { if (mDispatchInProgressCounter > 0) { mToBeAddedEventHandlers.put(key, - eventHandler as CalendarController.EventHandler) + eventHandler as CalendarController.EventHandler) } else { eventHandlers.put(key, eventHandler as CalendarController.EventHandler) } @@ -687,7 +687,7 @@ class CalendarController private constructor(context: Context) { const val MIN_CALENDAR_WEEK = 0 const val MAX_CALENDAR_WEEK = 3497 // weeks between 1/1/1970 and 1/1/2037 private val instances: WeakHashMap> = - WeakHashMap>() + WeakHashMap>() /** * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time @@ -704,7 +704,7 @@ class CalendarController private constructor(context: Context) { * * @param context The activity if at all possible. */ - @JvmStatic fun getInstance(context: Context): CalendarController { + @JvmStatic fun getInstance(context: Context?): CalendarController? { synchronized(instances) { var controller: CalendarController? = null val weakController: WeakReference? = instances.get(context) @@ -735,9 +735,9 @@ class CalendarController private constructor(context: Context) { mUpdateTimezone.run() mTime?.setToNow() mDetailViewType = Utils.getSharedPreference( - mContext, - GeneralPreferences.KEY_DETAILED_VIEW, - GeneralPreferences.DEFAULT_DETAILED_VIEW + mContext, + GeneralPreferences.KEY_DETAILED_VIEW, + GeneralPreferences.DEFAULT_DETAILED_VIEW ) } } \ No newline at end of file diff --git a/src/com/android/calendar/EventInfoActivity.kt b/src/com/android/calendar/EventInfoActivity.kt index 8c15b992..c0a1b9cd 100644 --- a/src/com/android/calendar/EventInfoActivity.kt +++ b/src/com/android/calendar/EventInfoActivity.kt @@ -76,8 +76,8 @@ class EventInfoActivity : Activity() { mStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0) mEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0) attendeeResponse = intent.getIntExtra( - ATTENDEE_STATUS, - Attendees.ATTENDEE_STATUS_NONE + ATTENDEE_STATUS, + Attendees.ATTENDEE_STATUS_NONE ) val data: Uri? = intent.getData() if (data != null) { @@ -117,10 +117,10 @@ class EventInfoActivity : Activity() { // close the activity and show the event in AllInOne. val res: Resources = getResources() if (!res.getBoolean(R.bool.agenda_show_event_info_full_screen) && - !res.getBoolean(R.bool.show_event_info_full_screen) + !res.getBoolean(R.bool.show_event_info_full_screen) ) { CalendarController.getInstance(this) - .launchViewEvent(mEventId, mStartMillis, mEndMillis, attendeeResponse) + ?.launchViewEvent(mEventId, mStartMillis, mEndMillis, attendeeResponse) finish() return } @@ -140,13 +140,13 @@ class EventInfoActivity : Activity() { val fragmentManager: FragmentManager = getFragmentManager() val ft: FragmentTransaction = fragmentManager.beginTransaction() mInfoFragment = EventInfoFragment( - this, - mEventId, - mStartMillis, - mEndMillis, - attendeeResponse, - isDialog, - if (isDialog) EventInfoFragment.DIALOG_WINDOW_STYLE + this, + mEventId, + mStartMillis, + mEndMillis, + attendeeResponse, + isDialog, + if (isDialog) EventInfoFragment.DIALOG_WINDOW_STYLE else EventInfoFragment.FULL_WINDOW_STYLE ) ft.replace(R.id.main_frame, mInfoFragment) @@ -174,8 +174,8 @@ class EventInfoActivity : Activity() { protected override fun onResume() { super.onResume() getContentResolver().registerContentObserver( - CalendarContract.Events.CONTENT_URI, - true, mObserver + CalendarContract.Events.CONTENT_URI, + true, mObserver ) } diff --git a/src/com/android/calendar/month/MonthByWeekFragment.kt b/src/com/android/calendar/month/MonthByWeekFragment.kt index d03b7464..7c1eec9a 100644 --- a/src/com/android/calendar/month/MonthByWeekFragment.kt +++ b/src/com/android/calendar/month/MonthByWeekFragment.kt @@ -53,10 +53,10 @@ import java.util.Calendar import java.util.HashMap class MonthByWeekFragment @JvmOverloads constructor( - initialTime: Long = System.currentTimeMillis(), - protected var mIsMiniMonth: Boolean = true + initialTime: Long = System.currentTimeMillis(), + protected var mIsMiniMonth: Boolean = true ) : SimpleDayPickerFragment(initialTime), CalendarController.EventHandler, - LoaderManager.LoaderCallbacks, OnScrollListener, OnTouchListener { + LoaderManager.LoaderCallbacks, OnScrollListener, OnTouchListener { protected var mMinimumTwoMonthFlingVelocity = 0f protected var mHideDeclined = false protected var mFirstLoadedJulianDay = 0 @@ -83,7 +83,7 @@ class MonthByWeekFragment @JvmOverloads constructor( mFirstVisibleDay.timezone = tz mFirstVisibleDay.normalize(true) if (mAdapter != null) { - mAdapter.refresh() + mAdapter?.refresh() } } } @@ -115,9 +115,9 @@ class MonthByWeekFragment @JvmOverloads constructor( override fun run() { if (!mIsDetached) { mLoader = getLoaderManager().initLoader( - 0, null, - this@MonthByWeekFragment - ) as CursorLoader + 0, null, + this@MonthByWeekFragment + ) as? CursorLoader } } } @@ -129,7 +129,7 @@ class MonthByWeekFragment @JvmOverloads constructor( * @return The new Uri to use */ private fun updateUri(): Uri { - val child: SimpleWeekView? = mListView.getChildAt(0) as? SimpleWeekView + val child: SimpleWeekView? = mListView?.getChildAt(0) as? SimpleWeekView if (child != null) { val julianDay: Int = child?.getFirstJulianDay() mFirstLoadedJulianDay = julianDay @@ -169,7 +169,7 @@ class MonthByWeekFragment @JvmOverloads constructor( var where = WHERE_CALENDARS_VISIBLE if (mHideDeclined || !mShowDetailsInMonth) { where += (" AND " + Instances.SELF_ATTENDEE_STATUS.toString() + "!=" + - Attendees.ATTENDEE_STATUS_DECLINED) + Attendees.ATTENDEE_STATUS_DECLINED) } return where } @@ -191,7 +191,7 @@ class MonthByWeekFragment @JvmOverloads constructor( super.onAttach(activity) mTZUpdater.run() if (mAdapter != null) { - mAdapter.setSelectedDay(mSelectedDay) + mAdapter?.setSelectedDay(mSelectedDay) } mIsDetached = false val viewConfig: ViewConfiguration = ViewConfiguration.get(activity) @@ -212,7 +212,7 @@ class MonthByWeekFragment @JvmOverloads constructor( super.onDetach() if (mShowCalendarControls) { if (mListView != null) { - mListView.removeCallbacks(mLoadingRunnable) + mListView?.removeCallbacks(mLoadingRunnable) } } } @@ -227,24 +227,24 @@ class MonthByWeekFragment @JvmOverloads constructor( weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek) weekParams.put(MonthByWeekAdapter.WEEK_PARAMS_IS_MINI, if (mIsMiniMonth) 1 else 0) weekParams.put( - SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, - Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) + SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, + Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) ) weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_DAYS_PER_WEEK, mDaysPerWeek) if (mAdapter == null) { mAdapter = MonthByWeekAdapter(getActivity(), weekParams) - mAdapter.registerDataSetObserver(mObserver) + mAdapter?.registerDataSetObserver(mObserver) } else { - mAdapter.updateParams(weekParams) + mAdapter?.updateParams(weekParams) } - mAdapter.notifyDataSetChanged() + mAdapter?.notifyDataSetChanged() } @Override override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? ): View { val v: View v = if (mIsMiniMonth) { @@ -252,27 +252,27 @@ class MonthByWeekFragment @JvmOverloads constructor( } else { inflater.inflate(R.layout.full_month_by_week, container, false) } - mDayNamesHeader = v.findViewById(R.id.day_names) as ViewGroup + mDayNamesHeader = v.findViewById(R.id.day_names) as? ViewGroup return v } @Override override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - mListView.setSelector(StateListDrawable()) - mListView.setOnTouchListener(this) + mListView?.setSelector(StateListDrawable()) + mListView?.setOnTouchListener(this) if (!mIsMiniMonth) { - mListView.setBackgroundColor(getResources().getColor(R.color.month_bgcolor)) + mListView?.setBackgroundColor(getResources().getColor(R.color.month_bgcolor)) } // To get a smoother transition when showing this fragment, delay loading of events until // the fragment is expended fully and the calendar controls are gone. if (mShowCalendarControls) { - mListView.postDelayed(mLoadingRunnable, mEventsLoadingDelay.toLong()) + mListView?.postDelayed(mLoadingRunnable, mEventsLoadingDelay.toLong()) } else { - mLoader = getLoaderManager().initLoader(0, null, this) as CursorLoader + mLoader = getLoaderManager().initLoader(0, null, this) as? CursorLoader } - mAdapter.setListView(mListView) + mAdapter?.setListView(mListView) } @Override @@ -284,8 +284,8 @@ class MonthByWeekFragment @JvmOverloads constructor( mDayLabels = arrayOfNulls(7) for (i in Calendar.SUNDAY..Calendar.SATURDAY) { mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString( - i, - DateUtils.LENGTH_MEDIUM + i, + DateUtils.LENGTH_MEDIUM ).toUpperCase() } } @@ -299,13 +299,13 @@ class MonthByWeekFragment @JvmOverloads constructor( var loader: CursorLoader? synchronized(mUpdateLoader) { mFirstLoadedJulianDay = - (Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) - - mNumWeeks * 7 / 2) + (Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) - + mNumWeeks * 7 / 2) mEventUri = updateUri() val where = updateWhere() loader = CursorLoader( - getActivity(), mEventUri, Event.EVENT_PROJECTION, where, - null /* WHERE_CALENDARS_SELECTED_ARGS */, INSTANCES_SORT_ORDER + getActivity(), mEventUri, Event.EVENT_PROJECTION, where, + null /* WHERE_CALENDARS_SELECTED_ARGS */, INSTANCES_SORT_ORDER ) loader?.setUpdateThrottle(LOADER_THROTTLE_DELAY.toLong()) } @@ -326,7 +326,7 @@ class MonthByWeekFragment @JvmOverloads constructor( } mDaysPerWeek = Utils.getDaysPerWeek(mContext) updateHeader() - mAdapter.setSelectedDay(mSelectedDay) + mAdapter?.setSelectedDay(mSelectedDay) mTZUpdater.run() mTodayUpdater.run() goTo(mSelectedDay.toMillis(true), false, true, false) @@ -337,8 +337,8 @@ class MonthByWeekFragment @JvmOverloads constructor( synchronized(mUpdateLoader) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d( - TAG, - "Found " + data?.getCount()?.toString() + " cursor entries for uri " + mEventUri + TAG, + "Found " + data?.getCount()?.toString() + " cursor entries for uri " + mEventUri ) } val cLoader: CursorLoader = loader as CursorLoader @@ -353,11 +353,11 @@ class MonthByWeekFragment @JvmOverloads constructor( } val events: ArrayList? = ArrayList() Event.buildEventsFromCursor( - events, data, mContext, mFirstLoadedJulianDay, mLastLoadedJulianDay + events, data, mContext, mFirstLoadedJulianDay, mLastLoadedJulianDay ) (mAdapter as MonthByWeekAdapter).setEvents( - mFirstLoadedJulianDay, - mLastLoadedJulianDay - mFirstLoadedJulianDay + 1, events + mFirstLoadedJulianDay, + mLastLoadedJulianDay - mFirstLoadedJulianDay + 1, events ) } } @@ -382,29 +382,29 @@ class MonthByWeekFragment @JvmOverloads constructor( if (event?.eventType === EventType.GO_TO) { var animate = true if (mDaysPerWeek * mNumWeeks * 2 < Math.abs( - Time.getJulianDay(event?.selectedTime?.toMillis(true) as Long, - event?.selectedTime?.gmtoff as Long) - - Time.getJulianDay(mFirstVisibleDay?.toMillis(true) as Long, - mFirstVisibleDay?.gmtoff as Long) - - mDaysPerWeek * mNumWeeks / 2L - ) + Time.getJulianDay(event?.selectedTime?.toMillis(true) as Long, + event?.selectedTime?.gmtoff as Long) - + Time.getJulianDay(mFirstVisibleDay?.toMillis(true) as Long, + mFirstVisibleDay?.gmtoff as Long) - + mDaysPerWeek * mNumWeeks / 2L + ) ) { animate = false } mDesiredDay.set(event?.selectedTime) mDesiredDay.normalize(true) val animateToday = event?.extraLong and - CalendarController.EXTRA_GOTO_TODAY.toLong() != 0L + CalendarController.EXTRA_GOTO_TODAY.toLong() != 0L val delayAnimation: Boolean = - goTo(event?.selectedTime?.toMillis(true)?.toLong() as Long, animate, true, false) + goTo(event?.selectedTime?.toMillis(true)?.toLong() as Long, animate, true, false) if (animateToday) { // If we need to flash today start the animation after any // movement from listView has ended. mHandler.postDelayed(object : Runnable { @Override override fun run() { - (mAdapter as MonthByWeekAdapter).animateToday() - mAdapter.notifyDataSetChanged() + (mAdapter as? MonthByWeekAdapter)?.animateToday() + mAdapter?.notifyDataSetChanged() } }, if (delayAnimation) GOTO_SCROLL_DURATION.toLong() else 0L) } @@ -418,29 +418,29 @@ class MonthByWeekFragment @JvmOverloads constructor( super.setMonthDisplayed(time, updateHighlight) if (!mIsMiniMonth) { var useSelected = false - if (time.year == mDesiredDay.year && time.month === mDesiredDay.month) { + if (time.year == mDesiredDay.year && time.month == mDesiredDay.month) { mSelectedDay.set(mDesiredDay) - mAdapter.setSelectedDay(mDesiredDay) + mAdapter?.setSelectedDay(mDesiredDay) useSelected = true } else { mSelectedDay.set(time) - mAdapter.setSelectedDay(time) + mAdapter?.setSelectedDay(time) } - val controller: CalendarController = CalendarController.getInstance(mContext) + val controller: CalendarController? = CalendarController.getInstance(mContext) if (mSelectedDay.minute >= 30) { mSelectedDay.minute = 30 } else { mSelectedDay.minute = 0 } val newTime: Long = mSelectedDay.normalize(true) - if (newTime != controller.time && mUserScrolled) { + if (newTime != controller?.time && mUserScrolled) { val offset: Long = - if (useSelected) 0 else DateUtils.WEEK_IN_MILLIS * mNumWeeks / 3.toLong() - controller.time = (newTime + offset) + if (useSelected) 0 else DateUtils.WEEK_IN_MILLIS * mNumWeeks / 3.toLong() + controller?.time = (newTime + offset) } - controller.sendEvent( - this as Object?, EventType.UPDATE_TITLE, time, time, time, -1, - ViewType.CURRENT, DateUtils.FORMAT_SHOW_DATE.toLong() or + controller?.sendEvent( + this as Object?, EventType.UPDATE_TITLE, time, time, time, -1, + ViewType.CURRENT, DateUtils.FORMAT_SHOW_DATE.toLong() or DateUtils.FORMAT_NO_MONTH_DAY.toLong() or DateUtils.FORMAT_SHOW_YEAR.toLong(), null, null ) @@ -479,7 +479,7 @@ class MonthByWeekFragment @JvmOverloads constructor( // Selection and selection args for adding event queries private val WHERE_CALENDARS_VISIBLE: String = Calendars.VISIBLE.toString() + "=1" private val INSTANCES_SORT_ORDER: String = (Instances.START_DAY.toString() + "," + - Instances.START_MINUTE + "," + Instances.TITLE) + Instances.START_MINUTE + "," + Instances.TITLE) protected var mShowDetailsInMonth = false private const val WEEKS_BUFFER = 1 -- cgit v1.2.3 From 530f46a9308034c31fb32811173e3c572309c690 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Tue, 29 Jun 2021 19:11:34 +0000 Subject: AOSP/Calendar - Add Kotlin code for SimpleDayPickerFragment.kt. Also uploaded corresponding Android.bp file A few functions required override modifiers and a few variable declarations to include nullability references. I also added @JvmField annotations to the instance variables in both classes. Because SimpleDayPickerFragment is a parent class, a few overriden methods were marked as "open" as opposed to "final". Test: manual (ran build and unit tests) and CTS testing suite $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.148 OK (22 tests) CTS TESTING: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ============================================ ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 14m 1s Total aggregated tests run time: 14m 1s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.45 tests/sec [376 tests / 841553 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 12894 ms || clean = 3057 ms Total preparation time: 12s || Total tear down time: 3s ======================================================= =============== Summary =============== Total Run time: 20m 22s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: I72d1ec3e631c9c79ebea306c72b857bdbbc8b5d3 --- Android.bp | 1 + .../calendar/month/SimpleDayPickerFragment.kt | 173 +++++++++++---------- 2 files changed, 93 insertions(+), 81 deletions(-) diff --git a/Android.bp b/Android.bp index 130217aa..92fe4a3f 100644 --- a/Android.bp +++ b/Android.bp @@ -40,6 +40,7 @@ exclude_srcsd = [ exclude_srcsm = [ "src/**/calendar/month/MonthListView.java", "src/**/calendar/month/SimpleWeekView.java", + "src/**/calendar/month/SimpleDayPickerFragment.java", "src/**/calendar/AsyncQueryServiceHelper.java", "src/**/calendar/CalendarApplication.java", "src/**/calendar/CalendarBackupAgent.java", diff --git a/src/com/android/calendar/month/SimpleDayPickerFragment.kt b/src/com/android/calendar/month/SimpleDayPickerFragment.kt index f8b38d5c..d3c43e66 100644 --- a/src/com/android/calendar/month/SimpleDayPickerFragment.kt +++ b/src/com/android/calendar/month/SimpleDayPickerFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -51,7 +51,7 @@ import java.util.Locale * easily display a month selection component in a given style. * */ -class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListener { +open class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListener { protected var WEEK_MIN_VISIBLE_HEIGHT = 12 protected var BOTTOM_BUFFER = 20 protected var mSaturdayColor = 0 @@ -59,34 +59,34 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe protected var mDayNameColor = 0 // You can override these numbers to get a different appearance - protected var mNumWeeks = 6 - protected var mShowWeekNumber = false - protected var mDaysPerWeek = 7 + @JvmField protected var mNumWeeks = 6 + @JvmField protected var mShowWeekNumber = false + @JvmField protected var mDaysPerWeek = 7 // These affect the scroll speed and feel protected var mFriction = 1.0f - protected var mContext: Context? = null - protected var mHandler: Handler + @JvmField protected var mContext: Context? = null + @JvmField protected var mHandler: Handler = Handler() protected var mMinimumFlingVelocity = 0f // highlighted time - protected var mSelectedDay: Time = Time() - protected var mAdapter: SimpleWeeksAdapter? = null - protected var mListView: ListView? = null - protected var mDayNamesHeader: ViewGroup? = null - protected var mDayLabels: Array + @JvmField protected var mSelectedDay: Time = Time() + @JvmField protected var mAdapter: SimpleWeeksAdapter? = null + @JvmField protected var mListView: ListView? = null + @JvmField protected var mDayNamesHeader: ViewGroup? = null + @JvmField protected var mDayLabels: Array = arrayOfNulls(7) // disposable variable used for time calculations - protected var mTempTime: Time = Time() + @JvmField protected var mTempTime: Time = Time() // When the week starts; numbered like Time. (e.g. SUNDAY=0). - protected var mFirstDayOfWeek = 0 + @JvmField protected var mFirstDayOfWeek = 0 // The first day of the focus month - protected var mFirstDayOfMonth: Time = Time() + @JvmField protected var mFirstDayOfMonth: Time = Time() // The first day that is visible in the view - protected var mFirstVisibleDay: Time = Time() + @JvmField protected var mFirstVisibleDay: Time = Time() // The name of the month to display protected var mMonthName: TextView? = null @@ -110,9 +110,9 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe protected var mCurrentScrollState: Int = OnScrollListener.SCROLL_STATE_IDLE // This causes an update of the view at midnight - protected var mTodayUpdater: Runnable = object : Runnable() { + @JvmField protected var mTodayUpdater: Runnable = object : Runnable { @Override - fun run() { + override fun run() { val midnight = Time(mFirstVisibleDay.timezone) midnight.setToNow() val currentMillis: Long = midnight.toMillis(true) @@ -121,18 +121,18 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe midnight.second = 0 midnight.monthDay++ val millisToMidnight: Long = midnight.normalize(true) - currentMillis - mHandler.postDelayed(this, millisToMidnight) + mHandler?.postDelayed(this, millisToMidnight) if (mAdapter != null) { - mAdapter.notifyDataSetChanged() + mAdapter?.notifyDataSetChanged() } } } // This allows us to update our position when a day is tapped - protected var mObserver: DataSetObserver = object : DataSetObserver() { + @JvmField protected var mObserver: DataSetObserver = object : DataSetObserver() { @Override - fun onChanged() { - val day: Time = mAdapter.getSelectedDay() + override fun onChanged() { + val day: Time = mAdapter!!.getSelectedDay() if (day.year !== mSelectedDay.year || day.yearDay !== mSelectedDay.yearDay) { goTo(day.toMillis(true), true, true, false) } @@ -140,12 +140,12 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe } @Override - fun onAttach(activity: Activity) { + override fun onAttach(activity: Activity) { super.onAttach(activity) mContext = activity val tz: String = Time.getCurrentTimezone() val viewConfig: ViewConfiguration = ViewConfiguration.get(activity) - mMinimumFlingVelocity = viewConfig.getScaledMinimumFlingVelocity() + mMinimumFlingVelocity = (viewConfig.getScaledMinimumFlingVelocity()).toFloat() // Ensure we're in the correct time zone mSelectedDay.switchTimezone(tz) @@ -177,8 +177,8 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe * Creates a new adapter if necessary and sets up its parameters. Override * this method to provide a custom adapter. */ - protected fun setUpAdapter() { - val weekParams: HashMap = HashMap() + protected open fun setUpAdapter() { + val weekParams: HashMap = HashMap() weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks) weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, if (mShowWeekNumber) 1 else 0) weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek) @@ -186,26 +186,29 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe Time.getJulianDay(mSelectedDay.toMillis(false), mSelectedDay.gmtoff)) if (mAdapter == null) { mAdapter = SimpleWeeksAdapter(getActivity(), weekParams) - mAdapter.registerDataSetObserver(mObserver) + mAdapter?.registerDataSetObserver(mObserver) } else { - mAdapter.updateParams(weekParams) + mAdapter?.updateParams(weekParams) } // refresh the view with the new parameters - mAdapter.notifyDataSetChanged() + mAdapter?.notifyDataSetChanged() } @Override - fun onCreate(savedInstanceState: Bundle?) { + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } @Override - fun onActivityCreated(savedInstanceState: Bundle?) { + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) setUpListView() setUpHeader() - mMonthName = getView().findViewById(R.id.month_name) as TextView - val child: SimpleWeekView = mListView.getChildAt(0) as SimpleWeekView ?: return + mMonthName = getView()?.findViewById(R.id.month_name) as? TextView + val child = mListView?.getChildAt(0) as? SimpleWeekView + if (child == null) { + return + } val julianDay: Int = child.getFirstJulianDay() mFirstVisibleDay.setJulianDay(julianDay) // set the title to the month of the second week @@ -217,7 +220,7 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe * Sets up the strings to be used by the header. Override this method to use * different strings or modify the view params. */ - protected fun setUpHeader() { + protected open fun setUpHeader() { mDayLabels = arrayOfNulls(7) for (i in Calendar.SUNDAY..Calendar.SATURDAY) { mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString(i, @@ -233,35 +236,35 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe // Configure the listview mListView = getListView() // Transparent background on scroll - mListView.setCacheColorHint(0) + mListView?.setCacheColorHint(0) // No dividers - mListView.setDivider(null) + mListView?.setDivider(null) // Items are clickable - mListView.setItemsCanFocus(true) + mListView?.setItemsCanFocus(true) // The thumb gets in the way, so disable it - mListView.setFastScrollEnabled(false) - mListView.setVerticalScrollBarEnabled(false) - mListView.setOnScrollListener(this) - mListView.setFadingEdgeLength(0) + mListView?.setFastScrollEnabled(false) + mListView?.setVerticalScrollBarEnabled(false) + mListView?.setOnScrollListener(this) + mListView?.setFadingEdgeLength(0) // Make the scrolling behavior nicer - mListView.setFriction(ViewConfiguration.getScrollFriction() * mFriction) + mListView?.setFriction(ViewConfiguration.getScrollFriction() * mFriction) } @Override - fun onResume() { + override fun onResume() { super.onResume() setUpAdapter() doResumeUpdates() } @Override - fun onPause() { + override fun onPause() { super.onPause() mHandler.removeCallbacks(mTodayUpdater) } @Override - fun onSaveInstanceState(outState: Bundle) { + override fun onSaveInstanceState(outState: Bundle) { outState.putLong(KEY_CURRENT_TIME, mSelectedDay.toMillis(true)) } @@ -269,14 +272,14 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe * Updates the user preference fields. Override this to use a different * preference space. */ - protected fun doResumeUpdates() { + protected open fun doResumeUpdates() { // Get default week start based on locale, subtracting one for use with android Time. val cal: Calendar = Calendar.getInstance(Locale.getDefault()) mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1 mShowWeekNumber = false updateHeader() goTo(mSelectedDay.toMillis(true), false, false, false) - mAdapter.setSelectedDay(mSelectedDay) + mAdapter?.setSelectedDay(mSelectedDay) mTodayUpdater.run() } @@ -285,7 +288,7 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe * label text. Override this to set up a custom header. */ protected fun updateHeader() { - var label: TextView = mDayNamesHeader.findViewById(R.id.wk_label) as TextView + var label: TextView = mDayNamesHeader!!.findViewById(R.id.wk_label) as TextView if (mShowWeekNumber) { label.setVisibility(View.VISIBLE) } else { @@ -293,7 +296,7 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe } val offset = mFirstDayOfWeek - 1 for (i in 1..7) { - label = mDayNamesHeader.getChildAt(i) as TextView + label = mDayNamesHeader!!.getChildAt(i) as TextView if (i < mDaysPerWeek + 1) { val position = (offset + i) % 7 label.setText(mDayLabels[position]) @@ -309,11 +312,12 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe label.setVisibility(View.GONE) } } - mDayNamesHeader.invalidate() + mDayNamesHeader?.invalidate() } @Override - fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View { val v: View = inflater.inflate(R.layout.month_by_week, container, false) mDayNamesHeader = v.findViewById(R.id.day_names) as ViewGroup @@ -370,12 +374,12 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe // TODO push Util function into Calendar public api. var position: Int = Utils.getWeeksSinceEpochFromJulianDay( Time.getJulianDay(millis, mTempTime.gmtoff), mFirstDayOfWeek) - var child: View + var child: View? var i = 0 var top = 0 // Find a child that's completely in the view do { - child = mListView.getChildAt(i++) + child = mListView?.getChildAt(i++) if (child == null) { break } @@ -388,7 +392,7 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe // Compute the first and last position visible val firstPosition: Int firstPosition = if (child != null) { - mListView.getPositionForView(child) + mListView!!.getPositionForView(child) } else { 0 } @@ -397,7 +401,7 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe lastPosition-- } if (setSelected) { - mAdapter.setSelectedDay(mSelectedDay) + mAdapter?.setSelectedDay(mSelectedDay) } if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "GoTo position $position") @@ -413,11 +417,11 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe Time.getJulianDay(millis, mFirstDayOfMonth.gmtoff), mFirstDayOfWeek) mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING if (animate) { - mListView.smoothScrollToPositionFromTop( + mListView?.smoothScrollToPositionFromTop( position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION) return true } else { - mListView.setSelectionFromTop(position, LIST_TOP_OFFSET) + mListView?.setSelectionFromTop(position, LIST_TOP_OFFSET) // Perform any after scroll operations that are needed onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE) } @@ -433,12 +437,16 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe * month. */ @Override - fun onScroll( + override fun onScroll( view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) { - val child: SimpleWeekView = view.getChildAt(0) as SimpleWeekView ?: return + val child = view.getChildAt(0) as? SimpleWeekView + if (child == null) { + return + } // Figure out where we are - val currScroll: Long = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom() + val currScroll: Long = (view.getFirstVisiblePosition() * child.getHeight() - + child.getBottom()).toLong() mFirstVisibleDay.setJulianDay(child.getFirstJulianDay()) // If we have moved since our last call update the direction @@ -451,7 +459,7 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe } mPreviousScrollPosition = currScroll mPreviousScrollState = mCurrentScrollState - updateMonthHighlight(mListView) + updateMonthHighlight(mListView as? AbsListView) } /** @@ -460,15 +468,18 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe * * @param view The ListView containing the weeks */ - private fun updateMonthHighlight(view: AbsListView) { - var child: SimpleWeekView = view.getChildAt(0) as SimpleWeekView ?: return + private fun updateMonthHighlight(view: AbsListView?) { + var child = view?.getChildAt(0) as? SimpleWeekView + if (child == null) { + return + } // Figure out where we are - val offset = if (child.getBottom() < WEEK_MIN_VISIBLE_HEIGHT) 1 else 0 + val offset = if (child?.getBottom() < WEEK_MIN_VISIBLE_HEIGHT) 1 else 0 // Use some hysteresis for checking which month to highlight. This // causes the month to transition when two full weeks of a month are // visible. - child = view.getChildAt(SCROLL_HYST_WEEKS + offset) as SimpleWeekView + child = view?.getChildAt(SCROLL_HYST_WEEKS + offset) as? SimpleWeekView if (child == null) { return } @@ -476,9 +487,9 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe // Find out which month we're moving into val month: Int month = if (mIsScrollingUp) { - child.getFirstMonth() + child?.getFirstMonth() } else { - child.getLastMonth() + child?.getLastMonth() } // And how it relates to our current highlighted month @@ -513,27 +524,27 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe * @param time A day in the new focus month. * @param updateHighlight TODO(epastern): */ - protected fun setMonthDisplayed(time: Time, updateHighlight: Boolean) { - val oldMonth: CharSequence = mMonthName.getText() - mMonthName.setText(Utils.formatMonthYear(mContext, time)) - mMonthName.invalidate() - if (!TextUtils.equals(oldMonth, mMonthName.getText())) { - mMonthName.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + protected open fun setMonthDisplayed(time: Time, updateHighlight: Boolean) { + val oldMonth: CharSequence = mMonthName!!.getText() + mMonthName?.setText(Utils.formatMonthYear(mContext, time)) + mMonthName?.invalidate() + if (!TextUtils.equals(oldMonth, mMonthName?.getText())) { + mMonthName?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } mCurrentMonthDisplayed = time.month if (updateHighlight) { - mAdapter.updateFocusMonth(mCurrentMonthDisplayed) + mAdapter?.updateFocusMonth(mCurrentMonthDisplayed) } } @Override - fun onScrollStateChanged(view: AbsListView?, scrollState: Int) { + override fun onScrollStateChanged(view: AbsListView?, scrollState: Int) { // use a post to prevent re-entering onScrollStateChanged before it // exits mScrollStateChangedRunnable.doScrollStateChange(view, scrollState) } - protected var mScrollStateChangedRunnable: ScrollStateRunnable = ScrollStateRunnable() + @JvmField protected var mScrollStateChangedRunnable: ScrollStateRunnable = ScrollStateRunnable() protected inner class ScrollStateRunnable : Runnable { private var mNewState = 0 @@ -548,10 +559,10 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe fun doScrollStateChange(view: AbsListView?, scrollState: Int) { mHandler.removeCallbacks(this) mNewState = scrollState - mHandler.postDelayed(this, SCROLL_CHANGE_DELAY) + mHandler.postDelayed(this, SCROLL_CHANGE_DELAY.toLong()) } - fun run() { + override fun run() { mCurrentScrollState = mNewState if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, @@ -561,7 +572,7 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe if (mNewState == OnScrollListener.SCROLL_STATE_IDLE && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) { mPreviousScrollState = mNewState - mAdapter.updateFocusMonth(mCurrentMonthDisplayed) + mAdapter?.updateFocusMonth(mCurrentMonthDisplayed) } else { mPreviousScrollState = mNewState } @@ -576,7 +587,7 @@ class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScrollListe protected const val SCROLL_HYST_WEEKS = 2 // How long the GoTo fling animation should last - protected const val GOTO_SCROLL_DURATION = 500 + @JvmStatic protected val GOTO_SCROLL_DURATION = 500 // How long to wait after receiving an onScrollStateChanged notification // before acting on it -- cgit v1.2.3 From 669e57093d87ef29b5778562812080046eea9561 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Wed, 30 Jun 2021 15:21:23 +0000 Subject: AOSP/Calendar - Copy of CalendarAppWidgetProvider.java The Java code in CalendarAppWidgetProvider.java has been copied into a corresponding .kt file. Test: manual - opening both files shows that they are both identical. Change-Id: Ia6ea76cd2bee15247af3e8f2dc8cb66984fce399 --- .../calendar/widget/CalendarAppWidgetProvider.kt | 230 +++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 src/com/android/calendar/widget/CalendarAppWidgetProvider.kt diff --git a/src/com/android/calendar/widget/CalendarAppWidgetProvider.kt b/src/com/android/calendar/widget/CalendarAppWidgetProvider.kt new file mode 100644 index 00000000..3a69efd3 --- /dev/null +++ b/src/com/android/calendar/widget/CalendarAppWidgetProvider.kt @@ -0,0 +1,230 @@ +/* + * 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.widget; + +import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; +import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; +import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.provider.CalendarContract; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Log; +import android.widget.RemoteViews; + +import com.android.calendar.AllInOneActivity; +import com.android.calendar.EventInfoActivity; +import com.android.calendar.R; +import com.android.calendar.Utils; + +/** + * Simple widget to show next upcoming calendar event. + */ +public class CalendarAppWidgetProvider extends AppWidgetProvider { + static final String TAG = "CalendarAppWidgetProvider"; + static final boolean LOGD = false; + + // TODO Move these to Calendar.java + static final String EXTRA_EVENT_IDS = "com.android.calendar.EXTRA_EVENT_IDS"; + + /** + * {@inheritDoc} + */ + @Override + public void onReceive(Context context, Intent intent) { + // Handle calendar-specific updates ourselves because they might be + // coming in without extras, which AppWidgetProvider then blocks. + final String action = intent.getAction(); + if (LOGD) + Log.d(TAG, "AppWidgetProvider got the intent: " + intent.toString()); + if (Utils.getWidgetUpdateAction(context).equals(action)) { + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + performUpdate(context, appWidgetManager, + appWidgetManager.getAppWidgetIds(getComponentName(context)), + null /* no eventIds */); + } else if (action != null && (action.equals(Intent.ACTION_PROVIDER_CHANGED) + || action.equals(Intent.ACTION_TIME_CHANGED) + || action.equals(Intent.ACTION_TIMEZONE_CHANGED) + || action.equals(Intent.ACTION_DATE_CHANGED) + || action.equals(Utils.getWidgetScheduledUpdateAction(context)))) { + Intent service = new Intent(context, CalendarAppWidgetService.class); + context.startService(service); + } else { + super.onReceive(context, intent); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onDisabled(Context context) { + // Unsubscribe from all AlarmManager updates + AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + PendingIntent pendingUpdate = getUpdateIntent(context); + am.cancel(pendingUpdate); + } + + /** + * {@inheritDoc} + */ + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + performUpdate(context, appWidgetManager, appWidgetIds, null /* no eventIds */); + } + + + /** + * Build {@link ComponentName} describing this specific + * {@link AppWidgetProvider} + */ + static ComponentName getComponentName(Context context) { + return new ComponentName(context, CalendarAppWidgetProvider.class); + } + + /** + * Process and push out an update for the given appWidgetIds. This call + * actually fires an intent to start {@link CalendarAppWidgetService} as a + * background service which handles the actual update, to prevent ANR'ing + * during database queries. + * + * @param context Context to use when starting {@link CalendarAppWidgetService}. + * @param appWidgetIds List of specific appWidgetIds to update, or null for + * all. + * @param changedEventIds Specific events known to be changed. If present, + * we use it to decide if an update is necessary. + */ + private void performUpdate(Context context, + AppWidgetManager appWidgetManager, int[] appWidgetIds, + long[] changedEventIds) { + // Launch over to service so it can perform update + for (int appWidgetId : appWidgetIds) { + if (LOGD) Log.d(TAG, "Building widget update..."); + Intent updateIntent = new Intent(context, CalendarAppWidgetService.class); + updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + if (changedEventIds != null) { + updateIntent.putExtra(EXTRA_EVENT_IDS, changedEventIds); + } + updateIntent.setData(Uri.parse(updateIntent.toUri(Intent.URI_INTENT_SCHEME))); + + RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget); + // Calendar header + Time time = new Time(Utils.getTimeZone(context, null)); + time.setToNow(); + long millis = time.toMillis(true); + final String dayOfWeek = DateUtils.getDayOfWeekString(time.weekDay + 1, + DateUtils.LENGTH_MEDIUM); + final String date = Utils.formatDateRange(context, millis, millis, + DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE + | DateUtils.FORMAT_NO_YEAR); + views.setTextViewText(R.id.day_of_week, dayOfWeek); + views.setTextViewText(R.id.date, date); + // Attach to list of events + views.setRemoteAdapter(appWidgetId, R.id.events_list, updateIntent); + appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.events_list); + + + // Launch calendar app when the user taps on the header + final Intent launchCalendarIntent = new Intent(Intent.ACTION_VIEW); + launchCalendarIntent.setClass(context, AllInOneActivity.class); + launchCalendarIntent + .setData(Uri.parse("content://com.android.calendar/time/" + millis)); + final PendingIntent launchCalendarPendingIntent = PendingIntent.getActivity( + context, 0 /* no requestCode */, launchCalendarIntent, 0 /* no flags */); + views.setOnClickPendingIntent(R.id.header, launchCalendarPendingIntent); + + // Each list item will call setOnClickExtra() to let the list know + // which item + // is selected by a user. + final PendingIntent updateEventIntent = getLaunchPendingIntentTemplate(context); + views.setPendingIntentTemplate(R.id.events_list, updateEventIntent); + + appWidgetManager.updateAppWidget(appWidgetId, views); + } + } + + /** + * Build the {@link PendingIntent} used to trigger an update of all calendar + * widgets. Uses {@link Utils#getWidgetScheduledUpdateAction(Context)} to + * directly target all widgets instead of using + * {@link AppWidgetManager#EXTRA_APPWIDGET_IDS}. + * + * @param context Context to use when building broadcast. + */ + static PendingIntent getUpdateIntent(Context context) { + Intent intent = new Intent(Utils.getWidgetScheduledUpdateAction(context)); + intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE); + return PendingIntent.getBroadcast(context, 0 /* no requestCode */, intent, + 0 /* no flags */); + } + + /** + * Build a {@link PendingIntent} to launch the Calendar app. This should be used + * in combination with {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)}. + */ + static PendingIntent getLaunchPendingIntentTemplate(Context context) { + Intent launchIntent = new Intent(); + launchIntent.setAction(Intent.ACTION_VIEW); + launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | + Intent.FLAG_ACTIVITY_TASK_ON_HOME); + launchIntent.setClass(context, AllInOneActivity.class); + return PendingIntent.getActivity(context, 0 /* no requestCode */, launchIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + } + + /** + * Build an {@link Intent} available as FillInIntent to launch the Calendar app. + * This should be used in combination with + * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}. + * If the go to time is 0, then calendar will be launched without a starting time. + * + * @param goToTime time that calendar should take the user to, or 0 to + * indicate no specific start time. + */ + static Intent getLaunchFillInIntent(Context context, long id, long start, long end, + boolean allDay) { + final Intent fillInIntent = new Intent(); + String dataString = "content://com.android.calendar/events"; + if (id != 0) { + fillInIntent.putExtra(Utils.INTENT_KEY_DETAIL_VIEW, true); + fillInIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | + Intent.FLAG_ACTIVITY_TASK_ON_HOME); + + dataString += "/" + id; + // If we have an event id - start the event info activity + fillInIntent.setClass(context, EventInfoActivity.class); + } else { + // If we do not have an event id - start AllInOne + fillInIntent.setClass(context, AllInOneActivity.class); + } + Uri data = Uri.parse(dataString); + fillInIntent.setData(data); + fillInIntent.putExtra(EXTRA_EVENT_BEGIN_TIME, start); + fillInIntent.putExtra(EXTRA_EVENT_END_TIME, end); + fillInIntent.putExtra(EXTRA_EVENT_ALL_DAY, allDay); + + return fillInIntent; + } +} -- cgit v1.2.3 From 0208a6ef298bc03ddd6b7979f5ee7b5bab8e1c39 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Wed, 30 Jun 2021 15:27:41 +0000 Subject: AOSP/Calendar - Initial Conversion of CalendarAppWidgetProvider This is how the automatic converter, built into Android Studio, converted CalendarAppWidgetProvider.java to Kotlin. Change-Id: I344d4c05c38f4199aad8cfef59838cb6d47625f1 --- .../calendar/widget/CalendarAppWidgetProvider.kt | 327 +++++++++++---------- 1 file changed, 170 insertions(+), 157 deletions(-) diff --git a/src/com/android/calendar/widget/CalendarAppWidgetProvider.kt b/src/com/android/calendar/widget/CalendarAppWidgetProvider.kt index 3a69efd3..eb5e5e53 100644 --- a/src/com/android/calendar/widget/CalendarAppWidgetProvider.kt +++ b/src/com/android/calendar/widget/CalendarAppWidgetProvider.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,66 +13,59 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar.widget; - -import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; -import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; -import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProvider; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.provider.CalendarContract; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.widget.RemoteViews; - -import com.android.calendar.AllInOneActivity; -import com.android.calendar.EventInfoActivity; -import com.android.calendar.R; -import com.android.calendar.Utils; +package com.android.calendar.widget + +import android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY +import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME +import android.provider.CalendarContract.EXTRA_EVENT_END_TIME +import android.app.AlarmManager +import android.app.PendingIntent +import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetProvider +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.provider.CalendarContract +import android.text.format.DateUtils +import android.text.format.Time +import android.util.Log +import android.widget.RemoteViews +import com.android.calendar.AllInOneActivity +import com.android.calendar.EventInfoActivity +import com.android.calendar.R +import com.android.calendar.Utils /** * Simple widget to show next upcoming calendar event. */ -public class CalendarAppWidgetProvider extends AppWidgetProvider { - static final String TAG = "CalendarAppWidgetProvider"; - static final boolean LOGD = false; - - // TODO Move these to Calendar.java - static final String EXTRA_EVENT_IDS = "com.android.calendar.EXTRA_EVENT_IDS"; - +class CalendarAppWidgetProvider : AppWidgetProvider() { /** * {@inheritDoc} */ @Override - public void onReceive(Context context, Intent intent) { + fun onReceive(context: Context, intent: Intent) { // Handle calendar-specific updates ourselves because they might be // coming in without extras, which AppWidgetProvider then blocks. - final String action = intent.getAction(); - if (LOGD) - Log.d(TAG, "AppWidgetProvider got the intent: " + intent.toString()); + val action: String = intent.getAction() + if (LOGD) Log.d(TAG, "AppWidgetProvider got the intent: " + intent.toString()) if (Utils.getWidgetUpdateAction(context).equals(action)) { - AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); - performUpdate(context, appWidgetManager, - appWidgetManager.getAppWidgetIds(getComponentName(context)), - null /* no eventIds */); + val appWidgetManager: AppWidgetManager = AppWidgetManager.getInstance(context) + performUpdate( + context, appWidgetManager, + appWidgetManager.getAppWidgetIds(getComponentName(context)), + null /* no eventIds */ + ) } else if (action != null && (action.equals(Intent.ACTION_PROVIDER_CHANGED) - || action.equals(Intent.ACTION_TIME_CHANGED) - || action.equals(Intent.ACTION_TIMEZONE_CHANGED) - || action.equals(Intent.ACTION_DATE_CHANGED) - || action.equals(Utils.getWidgetScheduledUpdateAction(context)))) { - Intent service = new Intent(context, CalendarAppWidgetService.class); - context.startService(service); + || action.equals(Intent.ACTION_TIME_CHANGED) + || action.equals(Intent.ACTION_TIMEZONE_CHANGED) + || action.equals(Intent.ACTION_DATE_CHANGED) + || action.equals(Utils.getWidgetScheduledUpdateAction(context))) + ) { + val service = Intent(context, CalendarAppWidgetService::class.java) + context.startService(service) } else { - super.onReceive(context, intent); + super.onReceive(context, intent) } } @@ -80,151 +73,171 @@ public class CalendarAppWidgetProvider extends AppWidgetProvider { * {@inheritDoc} */ @Override - public void onDisabled(Context context) { + fun onDisabled(context: Context) { // Unsubscribe from all AlarmManager updates - AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - PendingIntent pendingUpdate = getUpdateIntent(context); - am.cancel(pendingUpdate); + val am: AlarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + val pendingUpdate: PendingIntent = getUpdateIntent(context) + am.cancel(pendingUpdate) } /** * {@inheritDoc} */ @Override - public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { - performUpdate(context, appWidgetManager, appWidgetIds, null /* no eventIds */); - } - - - /** - * Build {@link ComponentName} describing this specific - * {@link AppWidgetProvider} - */ - static ComponentName getComponentName(Context context) { - return new ComponentName(context, CalendarAppWidgetProvider.class); + fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { + performUpdate(context, appWidgetManager, appWidgetIds, null /* no eventIds */) } /** * Process and push out an update for the given appWidgetIds. This call - * actually fires an intent to start {@link CalendarAppWidgetService} as a + * actually fires an intent to start [CalendarAppWidgetService] as a * background service which handles the actual update, to prevent ANR'ing * during database queries. * - * @param context Context to use when starting {@link CalendarAppWidgetService}. + * @param context Context to use when starting [CalendarAppWidgetService]. * @param appWidgetIds List of specific appWidgetIds to update, or null for - * all. + * all. * @param changedEventIds Specific events known to be changed. If present, - * we use it to decide if an update is necessary. + * we use it to decide if an update is necessary. */ - private void performUpdate(Context context, - AppWidgetManager appWidgetManager, int[] appWidgetIds, - long[] changedEventIds) { + private fun performUpdate( + context: Context, + appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, + changedEventIds: LongArray? + ) { // Launch over to service so it can perform update - for (int appWidgetId : appWidgetIds) { - if (LOGD) Log.d(TAG, "Building widget update..."); - Intent updateIntent = new Intent(context, CalendarAppWidgetService.class); - updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + for (appWidgetId in appWidgetIds) { + if (LOGD) Log.d(TAG, "Building widget update...") + val updateIntent = Intent(context, CalendarAppWidgetService::class.java) + updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) if (changedEventIds != null) { - updateIntent.putExtra(EXTRA_EVENT_IDS, changedEventIds); + updateIntent.putExtra(EXTRA_EVENT_IDS, changedEventIds) } - updateIntent.setData(Uri.parse(updateIntent.toUri(Intent.URI_INTENT_SCHEME))); - - RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget); + updateIntent.setData(Uri.parse(updateIntent.toUri(Intent.URI_INTENT_SCHEME))) + val views = RemoteViews(context.getPackageName(), R.layout.appwidget) // Calendar header - Time time = new Time(Utils.getTimeZone(context, null)); - time.setToNow(); - long millis = time.toMillis(true); - final String dayOfWeek = DateUtils.getDayOfWeekString(time.weekDay + 1, - DateUtils.LENGTH_MEDIUM); - final String date = Utils.formatDateRange(context, millis, millis, - DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE - | DateUtils.FORMAT_NO_YEAR); - views.setTextViewText(R.id.day_of_week, dayOfWeek); - views.setTextViewText(R.id.date, date); + val time = Time(Utils.getTimeZone(context, null)) + time.setToNow() + val millis: Long = time.toMillis(true) + val dayOfWeek: String = DateUtils.getDayOfWeekString( + time.weekDay + 1, + DateUtils.LENGTH_MEDIUM + ) + val date: String = Utils.formatDateRange( + context, millis, millis, + DateUtils.FORMAT_ABBREV_ALL or DateUtils.FORMAT_SHOW_DATE + or DateUtils.FORMAT_NO_YEAR + ) + views.setTextViewText(R.id.day_of_week, dayOfWeek) + views.setTextViewText(R.id.date, date) // Attach to list of events - views.setRemoteAdapter(appWidgetId, R.id.events_list, updateIntent); - appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.events_list); + views.setRemoteAdapter(appWidgetId, R.id.events_list, updateIntent) + appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.events_list) // Launch calendar app when the user taps on the header - final Intent launchCalendarIntent = new Intent(Intent.ACTION_VIEW); - launchCalendarIntent.setClass(context, AllInOneActivity.class); + val launchCalendarIntent = Intent(Intent.ACTION_VIEW) + launchCalendarIntent.setClass(context, AllInOneActivity::class.java) launchCalendarIntent - .setData(Uri.parse("content://com.android.calendar/time/" + millis)); - final PendingIntent launchCalendarPendingIntent = PendingIntent.getActivity( - context, 0 /* no requestCode */, launchCalendarIntent, 0 /* no flags */); - views.setOnClickPendingIntent(R.id.header, launchCalendarPendingIntent); + .setData(Uri.parse("content://com.android.calendar/time/$millis")) + val launchCalendarPendingIntent: PendingIntent = PendingIntent.getActivity( + context, 0 /* no requestCode */, launchCalendarIntent, 0 /* no flags */ + ) + views.setOnClickPendingIntent(R.id.header, launchCalendarPendingIntent) // Each list item will call setOnClickExtra() to let the list know // which item // is selected by a user. - final PendingIntent updateEventIntent = getLaunchPendingIntentTemplate(context); - views.setPendingIntentTemplate(R.id.events_list, updateEventIntent); - - appWidgetManager.updateAppWidget(appWidgetId, views); + val updateEventIntent: PendingIntent = getLaunchPendingIntentTemplate(context) + views.setPendingIntentTemplate(R.id.events_list, updateEventIntent) + appWidgetManager.updateAppWidget(appWidgetId, views) } } - /** - * Build the {@link PendingIntent} used to trigger an update of all calendar - * widgets. Uses {@link Utils#getWidgetScheduledUpdateAction(Context)} to - * directly target all widgets instead of using - * {@link AppWidgetManager#EXTRA_APPWIDGET_IDS}. - * - * @param context Context to use when building broadcast. - */ - static PendingIntent getUpdateIntent(Context context) { - Intent intent = new Intent(Utils.getWidgetScheduledUpdateAction(context)); - intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE); - return PendingIntent.getBroadcast(context, 0 /* no requestCode */, intent, - 0 /* no flags */); - } + companion object { + const val TAG = "CalendarAppWidgetProvider" + const val LOGD = false - /** - * Build a {@link PendingIntent} to launch the Calendar app. This should be used - * in combination with {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)}. - */ - static PendingIntent getLaunchPendingIntentTemplate(Context context) { - Intent launchIntent = new Intent(); - launchIntent.setAction(Intent.ACTION_VIEW); - launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | - Intent.FLAG_ACTIVITY_TASK_ON_HOME); - launchIntent.setClass(context, AllInOneActivity.class); - return PendingIntent.getActivity(context, 0 /* no requestCode */, launchIntent, - PendingIntent.FLAG_UPDATE_CURRENT); - } + // TODO Move these to Calendar.java + const val EXTRA_EVENT_IDS = "com.android.calendar.EXTRA_EVENT_IDS" - /** - * Build an {@link Intent} available as FillInIntent to launch the Calendar app. - * This should be used in combination with - * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}. - * If the go to time is 0, then calendar will be launched without a starting time. - * - * @param goToTime time that calendar should take the user to, or 0 to - * indicate no specific start time. - */ - static Intent getLaunchFillInIntent(Context context, long id, long start, long end, - boolean allDay) { - final Intent fillInIntent = new Intent(); - String dataString = "content://com.android.calendar/events"; - if (id != 0) { - fillInIntent.putExtra(Utils.INTENT_KEY_DETAIL_VIEW, true); - fillInIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | - Intent.FLAG_ACTIVITY_TASK_ON_HOME); + /** + * Build [ComponentName] describing this specific + * [AppWidgetProvider] + */ + fun getComponentName(context: Context?): ComponentName { + return ComponentName(context, CalendarAppWidgetProvider::class.java) + } - dataString += "/" + id; - // If we have an event id - start the event info activity - fillInIntent.setClass(context, EventInfoActivity.class); - } else { - // If we do not have an event id - start AllInOne - fillInIntent.setClass(context, AllInOneActivity.class); + /** + * Build the [PendingIntent] used to trigger an update of all calendar + * widgets. Uses [Utils.getWidgetScheduledUpdateAction] to + * directly target all widgets instead of using + * [AppWidgetManager.EXTRA_APPWIDGET_IDS]. + * + * @param context Context to use when building broadcast. + */ + fun getUpdateIntent(context: Context?): PendingIntent { + val intent = Intent(Utils.getWidgetScheduledUpdateAction(context)) + intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE) + return PendingIntent.getBroadcast( + context, 0 /* no requestCode */, intent, + 0 /* no flags */ + ) } - Uri data = Uri.parse(dataString); - fillInIntent.setData(data); - fillInIntent.putExtra(EXTRA_EVENT_BEGIN_TIME, start); - fillInIntent.putExtra(EXTRA_EVENT_END_TIME, end); - fillInIntent.putExtra(EXTRA_EVENT_ALL_DAY, allDay); - return fillInIntent; + /** + * Build a [PendingIntent] to launch the Calendar app. This should be used + * in combination with [RemoteViews.setPendingIntentTemplate]. + */ + fun getLaunchPendingIntentTemplate(context: Context?): PendingIntent { + val launchIntent = Intent() + launchIntent.setAction(Intent.ACTION_VIEW) + launchIntent.setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or + Intent.FLAG_ACTIVITY_TASK_ON_HOME + ) + launchIntent.setClass(context, AllInOneActivity::class.java) + return PendingIntent.getActivity( + context, 0 /* no requestCode */, launchIntent, + PendingIntent.FLAG_UPDATE_CURRENT + ) + } + + /** + * Build an [Intent] available as FillInIntent to launch the Calendar app. + * This should be used in combination with + * [RemoteViews.setOnClickFillInIntent]. + * If the go to time is 0, then calendar will be launched without a starting time. + * + * @param goToTime time that calendar should take the user to, or 0 to + * indicate no specific start time. + */ + fun getLaunchFillInIntent( + context: Context?, id: Long, start: Long, end: Long, + allDay: Boolean + ): Intent { + val fillInIntent = Intent() + var dataString = "content://com.android.calendar/events" + if (id != 0L) { + fillInIntent.putExtra(Utils.INTENT_KEY_DETAIL_VIEW, true) + fillInIntent.setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or + Intent.FLAG_ACTIVITY_TASK_ON_HOME + ) + dataString += "/$id" + // If we have an event id - start the event info activity + fillInIntent.setClass(context, EventInfoActivity::class.java) + } else { + // If we do not have an event id - start AllInOne + fillInIntent.setClass(context, AllInOneActivity::class.java) + } + val data: Uri = Uri.parse(dataString) + fillInIntent.setData(data) + fillInIntent.putExtra(EXTRA_EVENT_BEGIN_TIME, start) + fillInIntent.putExtra(EXTRA_EVENT_END_TIME, end) + fillInIntent.putExtra(EXTRA_EVENT_ALL_DAY, allDay) + return fillInIntent + } } -} +} \ No newline at end of file -- cgit v1.2.3 From 79b42da8dce68fbffb6abe874b3ea3142749201c Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Wed, 30 Jun 2021 17:17:34 +0000 Subject: AOSP/Calendar - CalendarAppWidgetProvider fully converted with bp file This is the fully converted and touched-up Kotlin file after the automatic converter was run. Apart from the "override" modifier being added to the correct methods, a few object references had to be casted to their non-nullable type to satisfy certain function parameters. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.162 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 8m 48s Total aggregated tests run time: 8m 48s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.71 tests/sec [376 tests / 528577 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 7044 ms || clean = 2096 ms Total preparation time: 7s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 10m 15s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: I3bd2f1f243a0af10191f77a8985485add8b2ee09 --- Android.bp | 1 + .../calendar/widget/CalendarAppWidgetProvider.kt | 66 ++++++++++++---------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/Android.bp b/Android.bp index 92fe4a3f..142971be 100644 --- a/Android.bp +++ b/Android.bp @@ -29,6 +29,7 @@ exclude_srcsd = [ "src/**/calendar/alerts/QuickResponseActivity.java", "src/**/calendar/month/MonthByWeekFragment.java", "src/**/calendar/widget/CalendarAppWidgetModel.java", + "src/**/calendar/widget/CalendarAppWidgetProvider.java", "src/**/calendar/CalendarController.java", "src/**/calendar/DayOfMonthDrawable.java", "src/**/calendar/Event.java", diff --git a/src/com/android/calendar/widget/CalendarAppWidgetProvider.kt b/src/com/android/calendar/widget/CalendarAppWidgetProvider.kt index eb5e5e53..b3539f22 100644 --- a/src/com/android/calendar/widget/CalendarAppWidgetProvider.kt +++ b/src/com/android/calendar/widget/CalendarAppWidgetProvider.kt @@ -44,26 +44,26 @@ class CalendarAppWidgetProvider : AppWidgetProvider() { * {@inheritDoc} */ @Override - fun onReceive(context: Context, intent: Intent) { + override fun onReceive(context: Context?, intent: Intent?) { // Handle calendar-specific updates ourselves because they might be // coming in without extras, which AppWidgetProvider then blocks. - val action: String = intent.getAction() + val action: String? = intent?.getAction() if (LOGD) Log.d(TAG, "AppWidgetProvider got the intent: " + intent.toString()) - if (Utils.getWidgetUpdateAction(context).equals(action)) { + if (Utils.getWidgetUpdateAction(context as Context).equals(action)) { val appWidgetManager: AppWidgetManager = AppWidgetManager.getInstance(context) performUpdate( - context, appWidgetManager, + context as Context, appWidgetManager, appWidgetManager.getAppWidgetIds(getComponentName(context)), null /* no eventIds */ ) - } else if (action != null && (action.equals(Intent.ACTION_PROVIDER_CHANGED) - || action.equals(Intent.ACTION_TIME_CHANGED) - || action.equals(Intent.ACTION_TIMEZONE_CHANGED) - || action.equals(Intent.ACTION_DATE_CHANGED) - || action.equals(Utils.getWidgetScheduledUpdateAction(context))) + } else if (action != null && (action.equals(Intent.ACTION_PROVIDER_CHANGED) || + action.equals(Intent.ACTION_TIME_CHANGED) || + action.equals(Intent.ACTION_TIMEZONE_CHANGED) || + action.equals(Intent.ACTION_DATE_CHANGED) || + action.equals(Utils.getWidgetScheduledUpdateAction(context as Context))) ) { val service = Intent(context, CalendarAppWidgetService::class.java) - context.startService(service) + context?.startService(service) } else { super.onReceive(context, intent) } @@ -73,7 +73,7 @@ class CalendarAppWidgetProvider : AppWidgetProvider() { * {@inheritDoc} */ @Override - fun onDisabled(context: Context) { + override fun onDisabled(context: Context) { // Unsubscribe from all AlarmManager updates val am: AlarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val pendingUpdate: PendingIntent = getUpdateIntent(context) @@ -84,8 +84,13 @@ class CalendarAppWidgetProvider : AppWidgetProvider() { * {@inheritDoc} */ @Override - fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { - performUpdate(context, appWidgetManager, appWidgetIds, null /* no eventIds */) + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray + ) { + performUpdate(context, appWidgetManager, + appWidgetIds, null /* no eventIds */) } /** @@ -102,7 +107,8 @@ class CalendarAppWidgetProvider : AppWidgetProvider() { */ private fun performUpdate( context: Context, - appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray, changedEventIds: LongArray? ) { // Launch over to service so it can perform update @@ -123,10 +129,10 @@ class CalendarAppWidgetProvider : AppWidgetProvider() { time.weekDay + 1, DateUtils.LENGTH_MEDIUM ) - val date: String = Utils.formatDateRange( + val date: String? = Utils.formatDateRange( context, millis, millis, DateUtils.FORMAT_ABBREV_ALL or DateUtils.FORMAT_SHOW_DATE - or DateUtils.FORMAT_NO_YEAR + or DateUtils.FORMAT_NO_YEAR ) views.setTextViewText(R.id.day_of_week, dayOfWeek) views.setTextViewText(R.id.date, date) @@ -134,7 +140,6 @@ class CalendarAppWidgetProvider : AppWidgetProvider() { views.setRemoteAdapter(appWidgetId, R.id.events_list, updateIntent) appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.events_list) - // Launch calendar app when the user taps on the header val launchCalendarIntent = Intent(Intent.ACTION_VIEW) launchCalendarIntent.setClass(context, AllInOneActivity::class.java) @@ -165,8 +170,8 @@ class CalendarAppWidgetProvider : AppWidgetProvider() { * Build [ComponentName] describing this specific * [AppWidgetProvider] */ - fun getComponentName(context: Context?): ComponentName { - return ComponentName(context, CalendarAppWidgetProvider::class.java) + @JvmStatic fun getComponentName(context: Context?): ComponentName { + return ComponentName(context as Context, CalendarAppWidgetProvider::class.java) } /** @@ -177,8 +182,8 @@ class CalendarAppWidgetProvider : AppWidgetProvider() { * * @param context Context to use when building broadcast. */ - fun getUpdateIntent(context: Context?): PendingIntent { - val intent = Intent(Utils.getWidgetScheduledUpdateAction(context)) + @JvmStatic fun getUpdateIntent(context: Context?): PendingIntent { + val intent = Intent(Utils.getWidgetScheduledUpdateAction(context as Context)) intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE) return PendingIntent.getBroadcast( context, 0 /* no requestCode */, intent, @@ -190,14 +195,14 @@ class CalendarAppWidgetProvider : AppWidgetProvider() { * Build a [PendingIntent] to launch the Calendar app. This should be used * in combination with [RemoteViews.setPendingIntentTemplate]. */ - fun getLaunchPendingIntentTemplate(context: Context?): PendingIntent { + @JvmStatic fun getLaunchPendingIntentTemplate(context: Context?): PendingIntent { val launchIntent = Intent() launchIntent.setAction(Intent.ACTION_VIEW) launchIntent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or - Intent.FLAG_ACTIVITY_TASK_ON_HOME + Intent.FLAG_ACTIVITY_TASK_ON_HOME ) - launchIntent.setClass(context, AllInOneActivity::class.java) + launchIntent.setClass(context as Context, AllInOneActivity::class.java) return PendingIntent.getActivity( context, 0 /* no requestCode */, launchIntent, PendingIntent.FLAG_UPDATE_CURRENT @@ -213,8 +218,11 @@ class CalendarAppWidgetProvider : AppWidgetProvider() { * @param goToTime time that calendar should take the user to, or 0 to * indicate no specific start time. */ - fun getLaunchFillInIntent( - context: Context?, id: Long, start: Long, end: Long, + @JvmStatic fun getLaunchFillInIntent( + context: Context?, + id: Long, + start: Long, + end: Long, allDay: Boolean ): Intent { val fillInIntent = Intent() @@ -223,14 +231,14 @@ class CalendarAppWidgetProvider : AppWidgetProvider() { fillInIntent.putExtra(Utils.INTENT_KEY_DETAIL_VIEW, true) fillInIntent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or - Intent.FLAG_ACTIVITY_TASK_ON_HOME + Intent.FLAG_ACTIVITY_TASK_ON_HOME ) dataString += "/$id" // If we have an event id - start the event info activity - fillInIntent.setClass(context, EventInfoActivity::class.java) + fillInIntent.setClass(context as Context, EventInfoActivity::class.java) } else { // If we do not have an event id - start AllInOne - fillInIntent.setClass(context, AllInOneActivity::class.java) + fillInIntent.setClass(context as Context, AllInOneActivity::class.java) } val data: Uri = Uri.parse(dataString) fillInIntent.setData(data) -- cgit v1.2.3 From 5610d36711b018362e2059d733481df33264a6b0 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Tue, 29 Jun 2021 16:52:00 +0000 Subject: AOSP/Calendar - Copy of AllInOneActivity.java The Java code in AllInOneActivity.java has been copied into a corresponding .kt file. Test: manual - opening both files shows that they are identical. Change-Id: Ie99a3056d89f2b285fe4fd4d0b5729680c97f24f --- src/com/android/calendar/AllInOneActivity.kt | 1062 ++++++++++++++++++++++++++ 1 file changed, 1062 insertions(+) create mode 100644 src/com/android/calendar/AllInOneActivity.kt diff --git a/src/com/android/calendar/AllInOneActivity.kt b/src/com/android/calendar/AllInOneActivity.kt new file mode 100644 index 00000000..cec6a40f --- /dev/null +++ b/src/com/android/calendar/AllInOneActivity.kt @@ -0,0 +1,1062 @@ +/* + * Copyright (C) 2010 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.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.ObjectAnimator; +import android.app.ActionBar; +import android.app.ActionBar.Tab; +import android.app.Activity; +import android.app.Fragment; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.content.AsyncQueryHandler; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.database.Cursor; +import android.graphics.drawable.LayerDrawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.provider.CalendarContract; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Events; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.RelativeLayout.LayoutParams; +import android.widget.TextView; + +import com.android.calendar.CalendarController.EventHandler; +import com.android.calendar.CalendarController.EventInfo; +import com.android.calendar.CalendarController.EventType; +import com.android.calendar.CalendarController.ViewType; +import com.android.calendar.month.MonthByWeekFragment; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; +import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; +import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; +import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; + +public class AllInOneActivity extends Activity implements EventHandler, + OnSharedPreferenceChangeListener, ActionBar.TabListener, + ActionBar.OnNavigationListener { + private static final String TAG = "AllInOneActivity"; + private static final boolean DEBUG = false; + private static final String EVENT_INFO_FRAGMENT_TAG = "EventInfoFragment"; + private static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time"; + private static final String BUNDLE_KEY_EVENT_ID = "key_event_id"; + private static final String BUNDLE_KEY_RESTORE_VIEW = "key_restore_view"; + private static final String BUNDLE_KEY_CHECK_ACCOUNTS = "key_check_for_accounts"; + private static final int HANDLER_KEY = 0; + + // Indices of buttons for the drop down menu (tabs replacement) + // Must match the strings in the array buttons_list in arrays.xml and the + // OnNavigationListener + private static final int BUTTON_DAY_INDEX = 0; + private static final int BUTTON_WEEK_INDEX = 1; + private static final int BUTTON_MONTH_INDEX = 2; + private static final int BUTTON_AGENDA_INDEX = 3; + + private CalendarController mController; + private static boolean mIsMultipane; + private static boolean mIsTabletConfig; + private boolean mOnSaveInstanceStateCalled = false; + private boolean mBackToPreviousView = false; + private ContentResolver mContentResolver; + private int mPreviousView; + private int mCurrentView; + private boolean mPaused = true; + private boolean mUpdateOnResume = false; + private boolean mHideControls = false; + private boolean mShowSideViews = true; + private boolean mShowWeekNum = false; + private TextView mHomeTime; + private TextView mDateRange; + private TextView mWeekTextView; + private View mMiniMonth; + private View mCalendarsList; + private View mMiniMonthContainer; + private View mSecondaryPane; + private String mTimeZone; + private boolean mShowCalendarControls; + private boolean mShowEventInfoFullScreen; + private int mWeekNum; + private int mCalendarControlsAnimationTime; + private int mControlsAnimateWidth; + private int mControlsAnimateHeight; + + private long mViewEventId = -1; + private long mIntentEventStartMillis = -1; + private long mIntentEventEndMillis = -1; + private int mIntentAttendeeResponse = Attendees.ATTENDEE_STATUS_NONE; + private boolean mIntentAllDay = false; + + // Action bar and Navigation bar (left side of Action bar) + private ActionBar mActionBar; + private ActionBar.Tab mDayTab; + private ActionBar.Tab mWeekTab; + private ActionBar.Tab mMonthTab; + private MenuItem mControlsMenu; + private Menu mOptionsMenu; + private CalendarViewAdapter mActionBarMenuSpinnerAdapter; + private QueryHandler mHandler; + private boolean mCheckForAccounts = true; + + private String mHideString; + private String mShowString; + + DayOfMonthDrawable mDayOfMonthIcon; + + int mOrientation; + + // Params for animating the controls on the right + private LayoutParams mControlsParams; + private LinearLayout.LayoutParams mVerticalControlsParams; + + private final AnimatorListener mSlideAnimationDoneListener = new AnimatorListener() { + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationEnd(android.animation.Animator animation) { + int visibility = mShowSideViews ? View.VISIBLE : View.GONE; + mMiniMonth.setVisibility(visibility); + mCalendarsList.setVisibility(visibility); + mMiniMonthContainer.setVisibility(visibility); + } + + @Override + public void onAnimationRepeat(android.animation.Animator animation) { + } + + @Override + public void onAnimationStart(android.animation.Animator animation) { + } + }; + + private class QueryHandler extends AsyncQueryHandler { + public QueryHandler(ContentResolver cr) { + super(cr); + } + + @Override + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + mCheckForAccounts = false; + try { + // If the query didn't return a cursor for some reason return + if (cursor == null || cursor.getCount() > 0 || isFinishing()) { + return; + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + + Bundle options = new Bundle(); + options.putCharSequence("introMessage", + getResources().getString(R.string.create_an_account_desc)); + options.putBoolean("allowSkip", true); + + AccountManager am = AccountManager.get(AllInOneActivity.this); + am.addAccount("com.google", CalendarContract.AUTHORITY, null, options, + AllInOneActivity.this, + new AccountManagerCallback() { + @Override + public void run(AccountManagerFuture future) { + } + }, null); + } + } + + private final Runnable mHomeTimeUpdater = new Runnable() { + @Override + public void run() { + mTimeZone = Utils.getTimeZone(AllInOneActivity.this, mHomeTimeUpdater); + updateSecondaryTitleFields(-1); + AllInOneActivity.this.invalidateOptionsMenu(); + Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone); + } + }; + + // runs every midnight/time changes and refreshes the today icon + private final Runnable mTimeChangesUpdater = new Runnable() { + @Override + public void run() { + mTimeZone = Utils.getTimeZone(AllInOneActivity.this, mHomeTimeUpdater); + AllInOneActivity.this.invalidateOptionsMenu(); + Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone); + } + }; + + + // 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 true; + } + + @Override + public void onChange(boolean selfChange) { + eventsChanged(); + } + }; + + @Override + protected void onNewIntent(Intent intent) { + String action = intent.getAction(); + if (DEBUG) + Log.d(TAG, "New intent received " + intent.toString()); + // Don't change the date if we're just returning to the app's home + if (Intent.ACTION_VIEW.equals(action) + && !intent.getBooleanExtra(Utils.INTENT_KEY_HOME, false)) { + long millis = parseViewAction(intent); + if (millis == -1) { + millis = Utils.timeFromIntentInMillis(intent); + } + if (millis != -1 && mViewEventId == -1 && mController != null) { + Time time = new Time(mTimeZone); + time.set(millis); + time.normalize(true); + mController.sendEvent(this, EventType.GO_TO, time, time, -1, ViewType.CURRENT); + } + } + } + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + if (icicle != null && icicle.containsKey(BUNDLE_KEY_CHECK_ACCOUNTS)) { + mCheckForAccounts = icicle.getBoolean(BUNDLE_KEY_CHECK_ACCOUNTS); + } + // Launch add google account if this is first time and there are no + // accounts yet + if (mCheckForAccounts) { + + mHandler = new QueryHandler(this.getContentResolver()); + mHandler.startQuery(0, null, Calendars.CONTENT_URI, new String[] { + Calendars._ID + }, null, null /* selection args */, null /* sort order */); + } + + // This needs to be created before setContentView + mController = CalendarController.getInstance(this); + + + // Get time from intent or icicle + long timeMillis = -1; + int viewType = -1; + final Intent intent = getIntent(); + if (icicle != null) { + timeMillis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME); + viewType = icicle.getInt(BUNDLE_KEY_RESTORE_VIEW, -1); + } else { + String action = intent.getAction(); + if (Intent.ACTION_VIEW.equals(action)) { + // Open EventInfo later + timeMillis = parseViewAction(intent); + } + + if (timeMillis == -1) { + timeMillis = Utils.timeFromIntentInMillis(intent); + } + } + + if (viewType == -1 || viewType > ViewType.MAX_VALUE) { + viewType = Utils.getViewTypeFromIntentAndSharedPref(this); + } + mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater); + Time t = new Time(mTimeZone); + t.set(timeMillis); + + if (DEBUG) { + if (icicle != null && intent != null) { + Log.d(TAG, "both, icicle:" + icicle.toString() + " intent:" + intent.toString()); + } else { + Log.d(TAG, "not both, icicle:" + icicle + " intent:" + intent); + } + } + + Resources res = getResources(); + mHideString = res.getString(R.string.hide_controls); + mShowString = res.getString(R.string.show_controls); + mOrientation = res.getConfiguration().orientation; + if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) { + mControlsAnimateWidth = (int)res.getDimension(R.dimen.calendar_controls_width); + if (mControlsParams == null) { + mControlsParams = new LayoutParams(mControlsAnimateWidth, 0); + } + mControlsParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + } else { + // Make sure width is in between allowed min and max width values + mControlsAnimateWidth = Math.max(res.getDisplayMetrics().widthPixels * 45 / 100, + (int)res.getDimension(R.dimen.min_portrait_calendar_controls_width)); + mControlsAnimateWidth = Math.min(mControlsAnimateWidth, + (int)res.getDimension(R.dimen.max_portrait_calendar_controls_width)); + } + + mControlsAnimateHeight = (int)res.getDimension(R.dimen.calendar_controls_height); + + mHideControls = true; + mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config); + mIsTabletConfig = Utils.getConfigBool(this, R.bool.tablet_config); + mShowCalendarControls = + Utils.getConfigBool(this, R.bool.show_calendar_controls); + mShowEventInfoFullScreen = + Utils.getConfigBool(this, R.bool.show_event_info_full_screen); + mCalendarControlsAnimationTime = res.getInteger(R.integer.calendar_controls_animation_time); + Utils.setAllowWeekForDetailView(mIsMultipane); + + // setContentView must be called before configureActionBar + setContentView(R.layout.all_in_one); + + if (mIsTabletConfig) { + mDateRange = (TextView) findViewById(R.id.date_bar); + mWeekTextView = (TextView) findViewById(R.id.week_num); + } else { + mDateRange = (TextView) getLayoutInflater().inflate(R.layout.date_range_title, null); + } + + // configureActionBar auto-selects the first tab you add, so we need to + // call it before we set up our own fragments to make sure it doesn't + // overwrite us + configureActionBar(viewType); + + mHomeTime = (TextView) findViewById(R.id.home_time); + mMiniMonth = findViewById(R.id.mini_month); + if (mIsTabletConfig && mOrientation == Configuration.ORIENTATION_PORTRAIT) { + mMiniMonth.setLayoutParams(new RelativeLayout.LayoutParams(mControlsAnimateWidth, + mControlsAnimateHeight)); + } + mCalendarsList = findViewById(R.id.calendar_list); + mMiniMonthContainer = findViewById(R.id.mini_month_container); + mSecondaryPane = findViewById(R.id.secondary_pane); + + // Must register as the first activity because this activity can modify + // the list of event handlers in it's handle method. This affects who + // the rest of the handlers the controller dispatches to are. + mController.registerFirstEventHandler(HANDLER_KEY, this); + + initFragments(timeMillis, viewType, icicle); + + // Listen for changes that would require this to be refreshed + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); + prefs.registerOnSharedPreferenceChangeListener(this); + + mContentResolver = getContentResolver(); + } + + private long parseViewAction(final Intent intent) { + long timeMillis = -1; + Uri data = intent.getData(); + if (data != null && data.isHierarchical()) { + List path = data.getPathSegments(); + if (path.size() == 2 && path.get(0).equals("events")) { + try { + mViewEventId = Long.valueOf(data.getLastPathSegment()); + if (mViewEventId != -1) { + mIntentEventStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0); + mIntentEventEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0); + mIntentAttendeeResponse = intent.getIntExtra( + ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); + mIntentAllDay = intent.getBooleanExtra(EXTRA_EVENT_ALL_DAY, false); + timeMillis = mIntentEventStartMillis; + } + } catch (NumberFormatException e) { + // Ignore if mViewEventId can't be parsed + } + } + } + return timeMillis; + } + + private void configureActionBar(int viewType) { + createButtonsSpinner(viewType, mIsTabletConfig); + if (mIsMultipane) { + mActionBar.setDisplayOptions( + ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME); + } else { + mActionBar.setDisplayOptions(0); + } + } + + private void createButtonsSpinner(int viewType, boolean tabletConfig) { + // If tablet configuration , show spinner with no dates + mActionBarMenuSpinnerAdapter = new CalendarViewAdapter (this, viewType, !tabletConfig); + mActionBar = getActionBar(); + mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); + mActionBar.setListNavigationCallbacks(mActionBarMenuSpinnerAdapter, this); + switch (viewType) { + case ViewType.AGENDA: + break; + case ViewType.DAY: + mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX); + break; + case ViewType.WEEK: + mActionBar.setSelectedNavigationItem(BUTTON_WEEK_INDEX); + break; + case ViewType.MONTH: + mActionBar.setSelectedNavigationItem(BUTTON_MONTH_INDEX); + break; + default: + mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX); + break; + } + } + // Clear buttons used in the agenda view + private void clearOptionsMenu() { + if (mOptionsMenu == null) { + return; + } + MenuItem cancelItem = mOptionsMenu.findItem(R.id.action_cancel); + if (cancelItem != null) { + cancelItem.setVisible(false); + } + } + + @Override + protected void onResume() { + super.onResume(); + + // Check if the upgrade code has ever been run. If not, force a sync just this one time. + Utils.trySyncAndDisableUpgradeReceiver(this); + + // Must register as the first activity because this activity can modify + // the list of event handlers in it's handle method. This affects who + // the rest of the handlers the controller dispatches to are. + mController.registerFirstEventHandler(HANDLER_KEY, this); + + mOnSaveInstanceStateCalled = false; + mContentResolver.registerContentObserver(CalendarContract.Events.CONTENT_URI, + true, mObserver); + if (mUpdateOnResume) { + initFragments(mController.getTime(), mController.getViewType(), null); + mUpdateOnResume = false; + } + Time t = new Time(mTimeZone); + t.set(mController.getTime()); + mController.sendEvent(this, EventType.UPDATE_TITLE, t, t, -1, ViewType.CURRENT, + mController.getDateFlags(), null, null); + // Make sure the drop-down menu will get its date updated at midnight + if (mActionBarMenuSpinnerAdapter != null) { + mActionBarMenuSpinnerAdapter.refresh(this); + } + + if (mControlsMenu != null) { + mControlsMenu.setTitle(mHideControls ? mShowString : mHideString); + } + mPaused = false; + + if (mViewEventId != -1 && mIntentEventStartMillis != -1 && mIntentEventEndMillis != -1) { + long currentMillis = System.currentTimeMillis(); + long selectedTime = -1; + if (currentMillis > mIntentEventStartMillis && currentMillis < mIntentEventEndMillis) { + selectedTime = currentMillis; + } + mController.sendEventRelatedEventWithExtra(this, EventType.VIEW_EVENT, mViewEventId, + mIntentEventStartMillis, mIntentEventEndMillis, -1, -1, + EventInfo.buildViewExtraLong(mIntentAttendeeResponse,mIntentAllDay), + selectedTime); + mViewEventId = -1; + mIntentEventStartMillis = -1; + mIntentEventEndMillis = -1; + mIntentAllDay = false; + } + Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone); + // Make sure the today icon is up to date + invalidateOptionsMenu(); + } + + @Override + protected void onPause() { + super.onPause(); + + mController.deregisterEventHandler(HANDLER_KEY); + mPaused = true; + mHomeTime.removeCallbacks(mHomeTimeUpdater); + if (mActionBarMenuSpinnerAdapter != null) { + mActionBarMenuSpinnerAdapter.onPause(); + } + mContentResolver.unregisterContentObserver(mObserver); + if (isFinishing()) { + // Stop listening for changes that would require this to be refreshed + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); + prefs.unregisterOnSharedPreferenceChangeListener(this); + } + // FRAG_TODO save highlighted days of the week; + if (mController.getViewType() != ViewType.EDIT) { + Utils.setDefaultView(this, mController.getViewType()); + } + Utils.resetMidnightUpdater(mHandler, mTimeChangesUpdater); + } + + @Override + protected void onUserLeaveHint() { + mController.sendEvent(this, EventType.USER_HOME, null, null, -1, ViewType.CURRENT); + super.onUserLeaveHint(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + mOnSaveInstanceStateCalled = true; + super.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); + prefs.unregisterOnSharedPreferenceChangeListener(this); + + mController.deregisterAllEventHandlers(); + + CalendarController.removeInstance(this); + } + + private void initFragments(long timeMillis, int viewType, Bundle icicle) { + if (DEBUG) { + Log.d(TAG, "Initializing to " + timeMillis + " for view " + viewType); + } + FragmentTransaction ft = getFragmentManager().beginTransaction(); + + if (mShowCalendarControls) { + Fragment miniMonthFrag = new MonthByWeekFragment(timeMillis, true); + ft.replace(R.id.mini_month, miniMonthFrag); + mController.registerEventHandler(R.id.mini_month, (EventHandler) miniMonthFrag); + } + if (!mShowCalendarControls || viewType == ViewType.EDIT) { + mMiniMonth.setVisibility(View.GONE); + mCalendarsList.setVisibility(View.GONE); + } + + EventInfo info = null; + if (viewType == ViewType.EDIT) { + mPreviousView = GeneralPreferences.getSharedPreferences(this).getInt( + GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW); + + long eventId = -1; + Intent intent = getIntent(); + Uri data = intent.getData(); + if (data != null) { + try { + eventId = Long.parseLong(data.getLastPathSegment()); + } catch (NumberFormatException e) { + if (DEBUG) { + Log.d(TAG, "Create new event"); + } + } + } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) { + eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID); + } + + long begin = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1); + long end = intent.getLongExtra(EXTRA_EVENT_END_TIME, -1); + info = new EventInfo(); + if (end != -1) { + info.endTime = new Time(); + info.endTime.set(end); + } + if (begin != -1) { + info.startTime = new Time(); + info.startTime.set(begin); + } + info.id = eventId; + // We set the viewtype so if the user presses back when they are + // done editing the controller knows we were in the Edit Event + // screen. Likewise for eventId + mController.setViewType(viewType); + mController.setEventId(eventId); + } else { + mPreviousView = viewType; + } + + setMainPane(ft, R.id.main_pane, viewType, timeMillis, true); + ft.commit(); // this needs to be after setMainPane() + + Time t = new Time(mTimeZone); + t.set(timeMillis); + if (viewType != ViewType.EDIT) { + mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType); + } + } + + @Override + public void onBackPressed() { + if (mCurrentView == ViewType.EDIT || mBackToPreviousView) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, mPreviousView); + } else { + super.onBackPressed(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + mOptionsMenu = menu; + getMenuInflater().inflate(R.menu.all_in_one_title_bar, menu); + + // Hide the "show/hide controls" button if this is a phone + // or the view type is "Month". + + mControlsMenu = menu.findItem(R.id.action_hide_controls); + if (!mShowCalendarControls) { + if (mControlsMenu != null) { + mControlsMenu.setVisible(false); + mControlsMenu.setEnabled(false); + } + } else if (mControlsMenu != null && mController != null + && (mController.getViewType() == ViewType.MONTH)) { + mControlsMenu.setVisible(false); + mControlsMenu.setEnabled(false); + } else if (mControlsMenu != null){ + mControlsMenu.setTitle(mHideControls ? mShowString : mHideString); + } + + MenuItem menuItem = menu.findItem(R.id.action_today); + if (Utils.isJellybeanOrLater()) { + // replace the default top layer drawable of the today icon with a + // custom drawable that shows the day of the month of today + LayerDrawable icon = (LayerDrawable) menuItem.getIcon(); + Utils.setTodayIcon(icon, this, mTimeZone); + } else { + menuItem.setIcon(R.drawable.ic_menu_today_no_date_holo_light); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + Time t = null; + int viewType = ViewType.CURRENT; + long extras = CalendarController.EXTRA_GOTO_TIME; + final int itemId = item.getItemId(); + if (itemId == R.id.action_today) { + viewType = ViewType.CURRENT; + t = new Time(mTimeZone); + t.setToNow(); + extras |= CalendarController.EXTRA_GOTO_TODAY; + } else if (itemId == R.id.action_hide_controls) { + mHideControls = !mHideControls; + item.setTitle(mHideControls ? mShowString : mHideString); + if (!mHideControls) { + mMiniMonth.setVisibility(View.VISIBLE); + mCalendarsList.setVisibility(View.VISIBLE); + mMiniMonthContainer.setVisibility(View.VISIBLE); + } + final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this, "controlsOffset", + mHideControls ? 0 : mControlsAnimateWidth, + mHideControls ? mControlsAnimateWidth : 0); + slideAnimation.setDuration(mCalendarControlsAnimationTime); + ObjectAnimator.setFrameDelay(0); + slideAnimation.start(); + return true; + } else { + Log.d(TAG, "Unsupported itemId: " + itemId); + return true; + } + mController.sendEvent(this, EventType.GO_TO, t, null, t, -1, viewType, extras, null, null); + return true; + } + + /** + * Sets the offset of the controls on the right for animating them off/on + * screen. ProGuard strips this if it's not in proguard.flags + * + * @param controlsOffset The current offset in pixels + */ + public void setControlsOffset(int controlsOffset) { + if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) { + mMiniMonth.setTranslationX(controlsOffset); + mCalendarsList.setTranslationX(controlsOffset); + mControlsParams.width = Math.max(0, mControlsAnimateWidth - controlsOffset); + mMiniMonthContainer.setLayoutParams(mControlsParams); + } else { + mMiniMonth.setTranslationY(controlsOffset); + mCalendarsList.setTranslationY(controlsOffset); + if (mVerticalControlsParams == null) { + mVerticalControlsParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, mControlsAnimateHeight); + } + mVerticalControlsParams.height = Math.max(0, mControlsAnimateHeight - controlsOffset); + mMiniMonthContainer.setLayoutParams(mVerticalControlsParams); + } + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + if (key.equals(GeneralPreferences.KEY_WEEK_START_DAY)) { + if (mPaused) { + mUpdateOnResume = true; + } else { + initFragments(mController.getTime(), mController.getViewType(), null); + } + } + } + + private void setMainPane( + FragmentTransaction ft, int viewId, int viewType, long timeMillis, boolean force) { + if (mOnSaveInstanceStateCalled) { + return; + } + if (!force && mCurrentView == viewType) { + return; + } + + // Remove this when transition to and from month view looks fine. + boolean doTransition = viewType != ViewType.MONTH && mCurrentView != ViewType.MONTH; + FragmentManager fragmentManager = getFragmentManager(); + + if (viewType != mCurrentView) { + // The rules for this previous view are different than the + // controller's and are used for intercepting the back button. + if (mCurrentView != ViewType.EDIT && mCurrentView > 0) { + mPreviousView = mCurrentView; + } + mCurrentView = viewType; + } + // Create new fragment + Fragment frag = null; + Fragment secFrag = null; + switch (viewType) { + case ViewType.AGENDA: + break; + case ViewType.DAY: + if (mActionBar != null && (mActionBar.getSelectedTab() != mDayTab)) { + mActionBar.selectTab(mDayTab); + } + if (mActionBarMenuSpinnerAdapter != null) { + mActionBar.setSelectedNavigationItem(CalendarViewAdapter.DAY_BUTTON_INDEX); + } + frag = new DayFragment(timeMillis, 1); + break; + case ViewType.MONTH: + if (mActionBar != null && (mActionBar.getSelectedTab() != mMonthTab)) { + mActionBar.selectTab(mMonthTab); + } + if (mActionBarMenuSpinnerAdapter != null) { + mActionBar.setSelectedNavigationItem(CalendarViewAdapter.MONTH_BUTTON_INDEX); + } + frag = new MonthByWeekFragment(timeMillis, false); + break; + case ViewType.WEEK: + default: + if (mActionBar != null && (mActionBar.getSelectedTab() != mWeekTab)) { + mActionBar.selectTab(mWeekTab); + } + if (mActionBarMenuSpinnerAdapter != null) { + mActionBar.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX); + } + frag = new DayFragment(timeMillis, 7); + break; + } + + // Update the current view so that the menu can update its look according to the + // current view. + if (mActionBarMenuSpinnerAdapter != null) { + mActionBarMenuSpinnerAdapter.setMainView(viewType); + if (!mIsTabletConfig) { + mActionBarMenuSpinnerAdapter.setTime(timeMillis); + } + } + + + // Show date only on tablet configurations in views different than Agenda + if (!mIsTabletConfig) { + mDateRange.setVisibility(View.GONE); + } else { + mDateRange.setVisibility(View.GONE); + } + + // Clear unnecessary buttons from the option menu when switching from the agenda view + if (viewType != ViewType.AGENDA) { + clearOptionsMenu(); + } + + boolean doCommit = false; + if (ft == null) { + doCommit = true; + ft = fragmentManager.beginTransaction(); + } + + if (doTransition) { + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); + } + + ft.replace(viewId, frag); + if (DEBUG) { + Log.d(TAG, "Adding handler with viewId " + viewId + " and type " + viewType); + } + // If the key is already registered this will replace it + mController.registerEventHandler(viewId, (EventHandler) frag); + + if (doCommit) { + if (DEBUG) { + Log.d(TAG, "setMainPane AllInOne=" + this + " finishing:" + this.isFinishing()); + } + ft.commit(); + } + } + + private void setTitleInActionBar(EventInfo event) { + if (event.eventType != EventType.UPDATE_TITLE || mActionBar == null) { + return; + } + + final long start = event.startTime.toMillis(false /* use isDst */); + final long end; + if (event.endTime != null) { + end = event.endTime.toMillis(false /* use isDst */); + } else { + end = start; + } + + final String msg = Utils.formatDateRange(this, start, end, (int) event.extraLong); + CharSequence oldDate = mDateRange.getText(); + mDateRange.setText(msg); + updateSecondaryTitleFields(event.selectedTime != null ? event.selectedTime.toMillis(true) + : start); + if (!TextUtils.equals(oldDate, msg)) { + mDateRange.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + if (mShowWeekNum && mWeekTextView != null) { + mWeekTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + } + } + } + + private void updateSecondaryTitleFields(long visibleMillisSinceEpoch) { + mShowWeekNum = Utils.getShowWeekNumber(this); + mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater); + if (visibleMillisSinceEpoch != -1) { + int weekNum = Utils.getWeekNumberFromTime(visibleMillisSinceEpoch, this); + mWeekNum = weekNum; + } + + if (mShowWeekNum && (mCurrentView == ViewType.WEEK) && mIsTabletConfig + && mWeekTextView != null) { + String weekString = getResources().getQuantityString(R.plurals.weekN, mWeekNum, + mWeekNum); + mWeekTextView.setText(weekString); + mWeekTextView.setVisibility(View.VISIBLE); + } else if (visibleMillisSinceEpoch != -1 && mWeekTextView != null + && mCurrentView == ViewType.DAY && mIsTabletConfig) { + Time time = new Time(mTimeZone); + time.set(visibleMillisSinceEpoch); + int julianDay = Time.getJulianDay(visibleMillisSinceEpoch, time.gmtoff); + time.setToNow(); + int todayJulianDay = Time.getJulianDay(time.toMillis(false), time.gmtoff); + String dayString = Utils.getDayOfWeekString(julianDay, todayJulianDay, + visibleMillisSinceEpoch, this); + mWeekTextView.setText(dayString); + mWeekTextView.setVisibility(View.VISIBLE); + } else if (mWeekTextView != null && (!mIsTabletConfig || mCurrentView != ViewType.DAY)) { + mWeekTextView.setVisibility(View.GONE); + } + + if (mHomeTime != null + && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK) + && !TextUtils.equals(mTimeZone, Time.getCurrentTimezone())) { + Time time = new Time(mTimeZone); + time.setToNow(); + long millis = time.toMillis(true); + boolean isDST = time.isDst != 0; + int flags = DateUtils.FORMAT_SHOW_TIME; + if (DateFormat.is24HourFormat(this)) { + flags |= DateUtils.FORMAT_24HOUR; + } + // Formats the time as + String timeString = (new StringBuilder( + Utils.formatDateRange(this, millis, millis, flags))).append(" ").append( + TimeZone.getTimeZone(mTimeZone).getDisplayName( + isDST, TimeZone.SHORT, Locale.getDefault())).toString(); + mHomeTime.setText(timeString); + mHomeTime.setVisibility(View.VISIBLE); + // Update when the minute changes + mHomeTime.removeCallbacks(mHomeTimeUpdater); + mHomeTime.postDelayed( + mHomeTimeUpdater, + DateUtils.MINUTE_IN_MILLIS - (millis % DateUtils.MINUTE_IN_MILLIS)); + } else if (mHomeTime != null) { + mHomeTime.setVisibility(View.GONE); + } + } + + @Override + public long getSupportedEventTypes() { + return EventType.GO_TO | EventType.UPDATE_TITLE; + } + + @Override + public void handleEvent(EventInfo event) { + long displayTime = -1; + if (event.eventType == EventType.GO_TO) { + if ((event.extraLong & CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS) != 0) { + mBackToPreviousView = true; + } else if (event.viewType != mController.getPreviousViewType() + && event.viewType != ViewType.EDIT) { + // Clear the flag is change to a different view type + mBackToPreviousView = false; + } + + setMainPane( + null, R.id.main_pane, event.viewType, event.startTime.toMillis(false), false); + if (mShowCalendarControls) { + int animationSize = (mOrientation == Configuration.ORIENTATION_LANDSCAPE) ? + mControlsAnimateWidth : mControlsAnimateHeight; + boolean noControlsView = event.viewType == ViewType.MONTH; + if (mControlsMenu != null) { + mControlsMenu.setVisible(!noControlsView); + mControlsMenu.setEnabled(!noControlsView); + } + if (noControlsView || mHideControls) { + // hide minimonth and calendar frag + mShowSideViews = false; + if (!mHideControls) { + final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this, + "controlsOffset", 0, animationSize); + slideAnimation.addListener(mSlideAnimationDoneListener); + slideAnimation.setDuration(mCalendarControlsAnimationTime); + ObjectAnimator.setFrameDelay(0); + slideAnimation.start(); + } else { + mMiniMonth.setVisibility(View.GONE); + mCalendarsList.setVisibility(View.GONE); + mMiniMonthContainer.setVisibility(View.GONE); + } + } else { + // show minimonth and calendar frag + mShowSideViews = true; + mMiniMonth.setVisibility(View.VISIBLE); + mCalendarsList.setVisibility(View.VISIBLE); + mMiniMonthContainer.setVisibility(View.VISIBLE); + if (!mHideControls && + (mController.getPreviousViewType() == ViewType.MONTH)) { + final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this, + "controlsOffset", animationSize, 0); + slideAnimation.setDuration(mCalendarControlsAnimationTime); + ObjectAnimator.setFrameDelay(0); + slideAnimation.start(); + } + } + } + displayTime = event.selectedTime != null ? event.selectedTime.toMillis(true) + : event.startTime.toMillis(true); + if (!mIsTabletConfig) { + mActionBarMenuSpinnerAdapter.setTime(displayTime); + } + } else if (event.eventType == EventType.UPDATE_TITLE) { + setTitleInActionBar(event); + if (!mIsTabletConfig) { + mActionBarMenuSpinnerAdapter.setTime(mController.getTime()); + } + } + updateSecondaryTitleFields(displayTime); + } + + @Override + public void eventsChanged() { + mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT); + } + + @Override + public void onTabSelected(Tab tab, FragmentTransaction ft) { + Log.w(TAG, "TabSelected AllInOne=" + this + " finishing:" + this.isFinishing()); + if (tab == mDayTab && mCurrentView != ViewType.DAY) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY); + } else if (tab == mWeekTab && mCurrentView != ViewType.WEEK) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK); + } else if (tab == mMonthTab && mCurrentView != ViewType.MONTH) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH); + } else { + Log.w(TAG, "TabSelected event from unknown tab: " + + (tab == null ? "null" : tab.getText())); + Log.w(TAG, "CurrentView:" + mCurrentView + " Tab:" + tab.toString() + " Day:" + mDayTab + + " Week:" + mWeekTab + " Month:" + mMonthTab); + } + } + + @Override + public void onTabReselected(Tab tab, FragmentTransaction ft) { + } + + @Override + public void onTabUnselected(Tab tab, FragmentTransaction ft) { + } + + + @Override + public boolean onNavigationItemSelected(int itemPosition, long itemId) { + switch (itemPosition) { + case CalendarViewAdapter.DAY_BUTTON_INDEX: + if (mCurrentView != ViewType.DAY) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY); + } + break; + case CalendarViewAdapter.WEEK_BUTTON_INDEX: + if (mCurrentView != ViewType.WEEK) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK); + } + break; + case CalendarViewAdapter.MONTH_BUTTON_INDEX: + if (mCurrentView != ViewType.MONTH) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH); + } + break; + case CalendarViewAdapter.AGENDA_BUTTON_INDEX: + break; + default: + Log.w(TAG, "ItemSelected event from unknown button: " + itemPosition); + Log.w(TAG, "CurrentView:" + mCurrentView + " Button:" + itemPosition + + " Day:" + mDayTab + " Week:" + mWeekTab + " Month:" + mMonthTab); + break; + } + return false; + } +} -- cgit v1.2.3 From fc4bb23bc9206523b3adcd04f2b9bf8e3b1855de Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Tue, 29 Jun 2021 16:54:45 +0000 Subject: AOSP/Calendar - Initial Conversion of AllInOneActivity This is how the automatic converter, built into Android Studio, converted AllInOneActivity.java to Kotlin. Change-Id: I8dc8b9a47c66ac6482eb771fc940d39836114875 --- src/com/android/calendar/AllInOneActivity.kt | 1314 ++++++++++++-------------- 1 file changed, 623 insertions(+), 691 deletions(-) diff --git a/src/com/android/calendar/AllInOneActivity.kt b/src/com/android/calendar/AllInOneActivity.kt index cec6a40f..d81f7b6d 100644 --- a/src/com/android/calendar/AllInOneActivity.kt +++ b/src/com/android/calendar/AllInOneActivity.kt @@ -13,697 +13,592 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar; - -import android.accounts.AccountManager; -import android.accounts.AccountManagerCallback; -import android.accounts.AccountManagerFuture; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.animation.ObjectAnimator; -import android.app.ActionBar; -import android.app.ActionBar.Tab; -import android.app.Activity; -import android.app.Fragment; -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.content.AsyncQueryHandler; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.database.Cursor; -import android.graphics.drawable.LayerDrawable; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.provider.CalendarContract; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.RelativeLayout.LayoutParams; -import android.widget.TextView; - -import com.android.calendar.CalendarController.EventHandler; -import com.android.calendar.CalendarController.EventInfo; -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.CalendarController.ViewType; -import com.android.calendar.month.MonthByWeekFragment; - -import java.io.IOException; -import java.util.List; -import java.util.Locale; -import java.util.TimeZone; - -import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; -import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; -import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; -import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; - -public class AllInOneActivity extends Activity implements EventHandler, - OnSharedPreferenceChangeListener, ActionBar.TabListener, - ActionBar.OnNavigationListener { - private static final String TAG = "AllInOneActivity"; - private static final boolean DEBUG = false; - private static final String EVENT_INFO_FRAGMENT_TAG = "EventInfoFragment"; - private static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time"; - private static final String BUNDLE_KEY_EVENT_ID = "key_event_id"; - private static final String BUNDLE_KEY_RESTORE_VIEW = "key_restore_view"; - private static final String BUNDLE_KEY_CHECK_ACCOUNTS = "key_check_for_accounts"; - private static final int HANDLER_KEY = 0; - - // Indices of buttons for the drop down menu (tabs replacement) - // Must match the strings in the array buttons_list in arrays.xml and the - // OnNavigationListener - private static final int BUTTON_DAY_INDEX = 0; - private static final int BUTTON_WEEK_INDEX = 1; - private static final int BUTTON_MONTH_INDEX = 2; - private static final int BUTTON_AGENDA_INDEX = 3; - - private CalendarController mController; - private static boolean mIsMultipane; - private static boolean mIsTabletConfig; - private boolean mOnSaveInstanceStateCalled = false; - private boolean mBackToPreviousView = false; - private ContentResolver mContentResolver; - private int mPreviousView; - private int mCurrentView; - private boolean mPaused = true; - private boolean mUpdateOnResume = false; - private boolean mHideControls = false; - private boolean mShowSideViews = true; - private boolean mShowWeekNum = false; - private TextView mHomeTime; - private TextView mDateRange; - private TextView mWeekTextView; - private View mMiniMonth; - private View mCalendarsList; - private View mMiniMonthContainer; - private View mSecondaryPane; - private String mTimeZone; - private boolean mShowCalendarControls; - private boolean mShowEventInfoFullScreen; - private int mWeekNum; - private int mCalendarControlsAnimationTime; - private int mControlsAnimateWidth; - private int mControlsAnimateHeight; - - private long mViewEventId = -1; - private long mIntentEventStartMillis = -1; - private long mIntentEventEndMillis = -1; - private int mIntentAttendeeResponse = Attendees.ATTENDEE_STATUS_NONE; - private boolean mIntentAllDay = false; +package com.android.calendar + +import android.accounts.AccountManager + +class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListener, + ActionBar.TabListener, ActionBar.OnNavigationListener { + private var mController: CalendarController? = null + private var mOnSaveInstanceStateCalled = false + private var mBackToPreviousView = false + private var mContentResolver: ContentResolver? = null + private var mPreviousView = 0 + private var mCurrentView = 0 + private var mPaused = true + private var mUpdateOnResume = false + private var mHideControls = false + private var mShowSideViews = true + private var mShowWeekNum = false + private var mHomeTime: TextView? = null + private var mDateRange: TextView? = null + private var mWeekTextView: TextView? = null + private var mMiniMonth: View? = null + private var mCalendarsList: View? = null + private var mMiniMonthContainer: View? = null + private var mSecondaryPane: View? = null + private var mTimeZone: String? = null + private var mShowCalendarControls = false + private var mShowEventInfoFullScreen = false + private var mWeekNum = 0 + private var mCalendarControlsAnimationTime = 0 + private var mControlsAnimateWidth = 0 + private var mControlsAnimateHeight = 0 + private var mViewEventId: Long = -1 + private var mIntentEventStartMillis: Long = -1 + private var mIntentEventEndMillis: Long = -1 + private var mIntentAttendeeResponse: Int = Attendees.ATTENDEE_STATUS_NONE + private var mIntentAllDay = false // Action bar and Navigation bar (left side of Action bar) - private ActionBar mActionBar; - private ActionBar.Tab mDayTab; - private ActionBar.Tab mWeekTab; - private ActionBar.Tab mMonthTab; - private MenuItem mControlsMenu; - private Menu mOptionsMenu; - private CalendarViewAdapter mActionBarMenuSpinnerAdapter; - private QueryHandler mHandler; - private boolean mCheckForAccounts = true; - - private String mHideString; - private String mShowString; - - DayOfMonthDrawable mDayOfMonthIcon; - - int mOrientation; + private var mActionBar: ActionBar? = null + private val mDayTab: Tab? = null + private val mWeekTab: Tab? = null + private val mMonthTab: Tab? = null + private var mControlsMenu: MenuItem? = null + private var mOptionsMenu: Menu? = null + private var mActionBarMenuSpinnerAdapter: CalendarViewAdapter? = null + private var mHandler: QueryHandler? = null + private var mCheckForAccounts = true + private var mHideString: String? = null + private var mShowString: String? = null + var mDayOfMonthIcon: DayOfMonthDrawable? = null + var mOrientation = 0 // Params for animating the controls on the right - private LayoutParams mControlsParams; - private LinearLayout.LayoutParams mVerticalControlsParams; - - private final AnimatorListener mSlideAnimationDoneListener = new AnimatorListener() { - + private var mControlsParams: LayoutParams? = null + private var mVerticalControlsParams: LinearLayout.LayoutParams? = null + private val mSlideAnimationDoneListener: AnimatorListener = object : AnimatorListener() { @Override - public void onAnimationCancel(Animator animation) { + fun onAnimationCancel(animation: Animator?) { } @Override - public void onAnimationEnd(android.animation.Animator animation) { - int visibility = mShowSideViews ? View.VISIBLE : View.GONE; - mMiniMonth.setVisibility(visibility); - mCalendarsList.setVisibility(visibility); - mMiniMonthContainer.setVisibility(visibility); + fun onAnimationEnd(animation: Animator?) { + val visibility: Int = if (mShowSideViews) View.VISIBLE else View.GONE + mMiniMonth.setVisibility(visibility) + mCalendarsList.setVisibility(visibility) + mMiniMonthContainer.setVisibility(visibility) } @Override - public void onAnimationRepeat(android.animation.Animator animation) { + fun onAnimationRepeat(animation: Animator?) { } @Override - public void onAnimationStart(android.animation.Animator animation) { - } - }; - - private class QueryHandler extends AsyncQueryHandler { - public QueryHandler(ContentResolver cr) { - super(cr); + fun onAnimationStart(animation: Animator?) { } + } + private inner class QueryHandler(cr: ContentResolver?) : AsyncQueryHandler(cr) { @Override - protected void onQueryComplete(int token, Object cookie, Cursor cursor) { - mCheckForAccounts = false; + protected fun onQueryComplete(token: Int, cookie: Object?, cursor: Cursor?) { + mCheckForAccounts = false try { // If the query didn't return a cursor for some reason return if (cursor == null || cursor.getCount() > 0 || isFinishing()) { - return; + return } } finally { if (cursor != null) { - cursor.close(); + cursor.close() } } - - Bundle options = new Bundle(); - options.putCharSequence("introMessage", - getResources().getString(R.string.create_an_account_desc)); - options.putBoolean("allowSkip", true); - - AccountManager am = AccountManager.get(AllInOneActivity.this); + val options = Bundle() + options.putCharSequence( + "introMessage", + getResources().getString(R.string.create_an_account_desc) + ) + options.putBoolean("allowSkip", true) + val am: AccountManager = AccountManager.get(this@AllInOneActivity) am.addAccount("com.google", CalendarContract.AUTHORITY, null, options, - AllInOneActivity.this, - new AccountManagerCallback() { - @Override - public void run(AccountManagerFuture future) { - } - }, null); + this@AllInOneActivity, + object : AccountManagerCallback() { + @Override + fun run(future: AccountManagerFuture?) { + } + }, null + ) } } - private final Runnable mHomeTimeUpdater = new Runnable() { + private val mHomeTimeUpdater: Runnable = object : Runnable() { @Override - public void run() { - mTimeZone = Utils.getTimeZone(AllInOneActivity.this, mHomeTimeUpdater); - updateSecondaryTitleFields(-1); - AllInOneActivity.this.invalidateOptionsMenu(); - Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone); + fun run() { + mTimeZone = Utils.getTimeZone(this@AllInOneActivity, this) + updateSecondaryTitleFields(-1) + this@AllInOneActivity.invalidateOptionsMenu() + Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone) } - }; + } // runs every midnight/time changes and refreshes the today icon - private final Runnable mTimeChangesUpdater = new Runnable() { + private val mTimeChangesUpdater: Runnable = object : Runnable() { @Override - public void run() { - mTimeZone = Utils.getTimeZone(AllInOneActivity.this, mHomeTimeUpdater); - AllInOneActivity.this.invalidateOptionsMenu(); - Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone); + fun run() { + mTimeZone = Utils.getTimeZone(this@AllInOneActivity, mHomeTimeUpdater) + this@AllInOneActivity.invalidateOptionsMenu() + Utils.setMidnightUpdater(mHandler, this, mTimeZone) } - }; - + } // Create an observer so that we can update the views whenever a // Calendar event changes. - private final ContentObserver mObserver = new ContentObserver(new Handler()) { + private val mObserver: ContentObserver = object : ContentObserver(Handler()) { @Override - public boolean deliverSelfNotifications() { - return true; + fun deliverSelfNotifications(): Boolean { + return true } @Override - public void onChange(boolean selfChange) { - eventsChanged(); + fun onChange(selfChange: Boolean) { + eventsChanged() } - }; + } @Override - protected void onNewIntent(Intent intent) { - String action = intent.getAction(); - if (DEBUG) - Log.d(TAG, "New intent received " + intent.toString()); + protected fun onNewIntent(intent: Intent) { + val action: String = intent.getAction() + if (DEBUG) Log.d(TAG, "New intent received " + intent.toString()) // Don't change the date if we're just returning to the app's home if (Intent.ACTION_VIEW.equals(action) - && !intent.getBooleanExtra(Utils.INTENT_KEY_HOME, false)) { - long millis = parseViewAction(intent); - if (millis == -1) { - millis = Utils.timeFromIntentInMillis(intent); + && !intent.getBooleanExtra(Utils.INTENT_KEY_HOME, false) + ) { + var millis = parseViewAction(intent) + if (millis == -1L) { + millis = Utils.timeFromIntentInMillis(intent) } - if (millis != -1 && mViewEventId == -1 && mController != null) { - Time time = new Time(mTimeZone); - time.set(millis); - time.normalize(true); - mController.sendEvent(this, EventType.GO_TO, time, time, -1, ViewType.CURRENT); + if (millis != -1L && mViewEventId == -1L && mController != null) { + val time = Time(mTimeZone) + time.set(millis) + time.normalize(true) + mController.sendEvent(this, EventType.GO_TO, time, time, -1, ViewType.CURRENT) } } } @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - + protected fun onCreate(icicle: Bundle?) { + super.onCreate(icicle) if (icicle != null && icicle.containsKey(BUNDLE_KEY_CHECK_ACCOUNTS)) { - mCheckForAccounts = icicle.getBoolean(BUNDLE_KEY_CHECK_ACCOUNTS); + mCheckForAccounts = icicle.getBoolean(BUNDLE_KEY_CHECK_ACCOUNTS) } // Launch add google account if this is first time and there are no // accounts yet if (mCheckForAccounts) { - - mHandler = new QueryHandler(this.getContentResolver()); - mHandler.startQuery(0, null, Calendars.CONTENT_URI, new String[] { - Calendars._ID - }, null, null /* selection args */, null /* sort order */); + mHandler = QueryHandler(this.getContentResolver()) + mHandler.startQuery( + 0, null, Calendars.CONTENT_URI, arrayOf( + Calendars._ID + ), null, null /* selection args */, null /* sort order */ + ) } // This needs to be created before setContentView - mController = CalendarController.getInstance(this); + mController = CalendarController.getInstance(this) // Get time from intent or icicle - long timeMillis = -1; - int viewType = -1; - final Intent intent = getIntent(); + var timeMillis: Long = -1 + var viewType = -1 + val intent: Intent = getIntent() if (icicle != null) { - timeMillis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME); - viewType = icicle.getInt(BUNDLE_KEY_RESTORE_VIEW, -1); + timeMillis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME) + viewType = icicle.getInt(BUNDLE_KEY_RESTORE_VIEW, -1) } else { - String action = intent.getAction(); + val action: String = intent.getAction() if (Intent.ACTION_VIEW.equals(action)) { // Open EventInfo later - timeMillis = parseViewAction(intent); + timeMillis = parseViewAction(intent) } - - if (timeMillis == -1) { - timeMillis = Utils.timeFromIntentInMillis(intent); + if (timeMillis == -1L) { + timeMillis = Utils.timeFromIntentInMillis(intent) } } - if (viewType == -1 || viewType > ViewType.MAX_VALUE) { - viewType = Utils.getViewTypeFromIntentAndSharedPref(this); + viewType = Utils.getViewTypeFromIntentAndSharedPref(this) } - mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater); - Time t = new Time(mTimeZone); - t.set(timeMillis); - + mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater) + val t = Time(mTimeZone) + t.set(timeMillis) if (DEBUG) { if (icicle != null && intent != null) { - Log.d(TAG, "both, icicle:" + icicle.toString() + " intent:" + intent.toString()); + Log.d( + TAG, + "both, icicle:" + icicle.toString().toString() + " intent:" + intent.toString() + ) } else { - Log.d(TAG, "not both, icicle:" + icicle + " intent:" + intent); + Log.d(TAG, "not both, icicle:$icicle intent:$intent") } } - - Resources res = getResources(); - mHideString = res.getString(R.string.hide_controls); - mShowString = res.getString(R.string.show_controls); - mOrientation = res.getConfiguration().orientation; + val res: Resources = getResources() + mHideString = res.getString(R.string.hide_controls) + mShowString = res.getString(R.string.show_controls) + mOrientation = res.getConfiguration().orientation if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) { - mControlsAnimateWidth = (int)res.getDimension(R.dimen.calendar_controls_width); + mControlsAnimateWidth = res.getDimension(R.dimen.calendar_controls_width) if (mControlsParams == null) { - mControlsParams = new LayoutParams(mControlsAnimateWidth, 0); + mControlsParams = LayoutParams(mControlsAnimateWidth, 0) } - mControlsParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + mControlsParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT) } else { // Make sure width is in between allowed min and max width values - mControlsAnimateWidth = Math.max(res.getDisplayMetrics().widthPixels * 45 / 100, - (int)res.getDimension(R.dimen.min_portrait_calendar_controls_width)); - mControlsAnimateWidth = Math.min(mControlsAnimateWidth, - (int)res.getDimension(R.dimen.max_portrait_calendar_controls_width)); - } - - mControlsAnimateHeight = (int)res.getDimension(R.dimen.calendar_controls_height); - - mHideControls = true; - mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config); - mIsTabletConfig = Utils.getConfigBool(this, R.bool.tablet_config); - mShowCalendarControls = - Utils.getConfigBool(this, R.bool.show_calendar_controls); - mShowEventInfoFullScreen = - Utils.getConfigBool(this, R.bool.show_event_info_full_screen); - mCalendarControlsAnimationTime = res.getInteger(R.integer.calendar_controls_animation_time); - Utils.setAllowWeekForDetailView(mIsMultipane); + mControlsAnimateWidth = Math.max( + res.getDisplayMetrics().widthPixels * 45 / 100, + res.getDimension(R.dimen.min_portrait_calendar_controls_width) as Int + ) + mControlsAnimateWidth = Math.min( + mControlsAnimateWidth, + res.getDimension(R.dimen.max_portrait_calendar_controls_width) as Int + ) + } + mControlsAnimateHeight = res.getDimension(R.dimen.calendar_controls_height) + mHideControls = true + mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config) + mIsTabletConfig = Utils.getConfigBool(this, R.bool.tablet_config) + mShowCalendarControls = Utils.getConfigBool(this, R.bool.show_calendar_controls) + mShowEventInfoFullScreen = Utils.getConfigBool(this, R.bool.show_event_info_full_screen) + mCalendarControlsAnimationTime = res.getInteger(R.integer.calendar_controls_animation_time) + Utils.setAllowWeekForDetailView(mIsMultipane) // setContentView must be called before configureActionBar - setContentView(R.layout.all_in_one); - + setContentView(R.layout.all_in_one) if (mIsTabletConfig) { - mDateRange = (TextView) findViewById(R.id.date_bar); - mWeekTextView = (TextView) findViewById(R.id.week_num); + mDateRange = findViewById(R.id.date_bar) as TextView? + mWeekTextView = findViewById(R.id.week_num) as TextView? } else { - mDateRange = (TextView) getLayoutInflater().inflate(R.layout.date_range_title, null); + mDateRange = getLayoutInflater().inflate(R.layout.date_range_title, null) as TextView } // configureActionBar auto-selects the first tab you add, so we need to // call it before we set up our own fragments to make sure it doesn't // overwrite us - configureActionBar(viewType); - - mHomeTime = (TextView) findViewById(R.id.home_time); - mMiniMonth = findViewById(R.id.mini_month); + configureActionBar(viewType) + mHomeTime = findViewById(R.id.home_time) as TextView? + mMiniMonth = findViewById(R.id.mini_month) if (mIsTabletConfig && mOrientation == Configuration.ORIENTATION_PORTRAIT) { - mMiniMonth.setLayoutParams(new RelativeLayout.LayoutParams(mControlsAnimateWidth, - mControlsAnimateHeight)); + mMiniMonth.setLayoutParams( + LayoutParams( + mControlsAnimateWidth, + mControlsAnimateHeight + ) + ) } - mCalendarsList = findViewById(R.id.calendar_list); - mMiniMonthContainer = findViewById(R.id.mini_month_container); - mSecondaryPane = findViewById(R.id.secondary_pane); + mCalendarsList = findViewById(R.id.calendar_list) + mMiniMonthContainer = findViewById(R.id.mini_month_container) + mSecondaryPane = findViewById(R.id.secondary_pane) // Must register as the first activity because this activity can modify // the list of event handlers in it's handle method. This affects who // the rest of the handlers the controller dispatches to are. - mController.registerFirstEventHandler(HANDLER_KEY, this); - - initFragments(timeMillis, viewType, icicle); + mController.registerFirstEventHandler(HANDLER_KEY, this) + initFragments(timeMillis, viewType, icicle) // Listen for changes that would require this to be refreshed - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); - prefs.registerOnSharedPreferenceChangeListener(this); - - mContentResolver = getContentResolver(); + val prefs: SharedPreferences = GeneralPreferences.getSharedPreferences(this) + prefs.registerOnSharedPreferenceChangeListener(this) + mContentResolver = getContentResolver() } - private long parseViewAction(final Intent intent) { - long timeMillis = -1; - Uri data = intent.getData(); + private fun parseViewAction(intent: Intent?): Long { + var timeMillis: Long = -1 + val data: Uri = intent.getData() if (data != null && data.isHierarchical()) { - List path = data.getPathSegments(); - if (path.size() == 2 && path.get(0).equals("events")) { + val path: List = data.getPathSegments() + if (path.size() === 2 && path[0].equals("events")) { try { - mViewEventId = Long.valueOf(data.getLastPathSegment()); - if (mViewEventId != -1) { - mIntentEventStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0); - mIntentEventEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0); + mViewEventId = Long.valueOf(data.getLastPathSegment()) + if (mViewEventId != -1L) { + mIntentEventStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0) + mIntentEventEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0) mIntentAttendeeResponse = intent.getIntExtra( - ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); - mIntentAllDay = intent.getBooleanExtra(EXTRA_EVENT_ALL_DAY, false); - timeMillis = mIntentEventStartMillis; + ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE + ) + mIntentAllDay = intent.getBooleanExtra(EXTRA_EVENT_ALL_DAY, false) + timeMillis = mIntentEventStartMillis } - } catch (NumberFormatException e) { + } catch (e: NumberFormatException) { // Ignore if mViewEventId can't be parsed } } } - return timeMillis; + return timeMillis } - private void configureActionBar(int viewType) { - createButtonsSpinner(viewType, mIsTabletConfig); + private fun configureActionBar(viewType: Int) { + createButtonsSpinner(viewType, mIsTabletConfig) if (mIsMultipane) { mActionBar.setDisplayOptions( - ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME); + ActionBar.DISPLAY_SHOW_CUSTOM or ActionBar.DISPLAY_SHOW_HOME + ) } else { - mActionBar.setDisplayOptions(0); + mActionBar.setDisplayOptions(0) } } - private void createButtonsSpinner(int viewType, boolean tabletConfig) { + private fun createButtonsSpinner(viewType: Int, tabletConfig: Boolean) { // If tablet configuration , show spinner with no dates - mActionBarMenuSpinnerAdapter = new CalendarViewAdapter (this, viewType, !tabletConfig); - mActionBar = getActionBar(); - mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); - mActionBar.setListNavigationCallbacks(mActionBarMenuSpinnerAdapter, this); - switch (viewType) { - case ViewType.AGENDA: - break; - case ViewType.DAY: - mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX); - break; - case ViewType.WEEK: - mActionBar.setSelectedNavigationItem(BUTTON_WEEK_INDEX); - break; - case ViewType.MONTH: - mActionBar.setSelectedNavigationItem(BUTTON_MONTH_INDEX); - break; - default: - mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX); - break; - } + mActionBarMenuSpinnerAdapter = CalendarViewAdapter(this, viewType, !tabletConfig) + mActionBar = getActionBar() + mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST) + mActionBar.setListNavigationCallbacks(mActionBarMenuSpinnerAdapter, this) + when (viewType) { + ViewType.AGENDA -> { + } + ViewType.DAY -> mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX) + ViewType.WEEK -> mActionBar.setSelectedNavigationItem(BUTTON_WEEK_INDEX) + ViewType.MONTH -> mActionBar.setSelectedNavigationItem(BUTTON_MONTH_INDEX) + else -> mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX) + } } + // Clear buttons used in the agenda view - private void clearOptionsMenu() { + private fun clearOptionsMenu() { if (mOptionsMenu == null) { - return; + return } - MenuItem cancelItem = mOptionsMenu.findItem(R.id.action_cancel); + val cancelItem: MenuItem = mOptionsMenu.findItem(R.id.action_cancel) if (cancelItem != null) { - cancelItem.setVisible(false); + cancelItem.setVisible(false) } } @Override - protected void onResume() { - super.onResume(); + protected fun onResume() { + super.onResume() // Check if the upgrade code has ever been run. If not, force a sync just this one time. - Utils.trySyncAndDisableUpgradeReceiver(this); + Utils.trySyncAndDisableUpgradeReceiver(this) // Must register as the first activity because this activity can modify // the list of event handlers in it's handle method. This affects who // the rest of the handlers the controller dispatches to are. - mController.registerFirstEventHandler(HANDLER_KEY, this); - - mOnSaveInstanceStateCalled = false; - mContentResolver.registerContentObserver(CalendarContract.Events.CONTENT_URI, - true, mObserver); + mController.registerFirstEventHandler(HANDLER_KEY, this) + mOnSaveInstanceStateCalled = false + mContentResolver.registerContentObserver( + CalendarContract.Events.CONTENT_URI, + true, mObserver + ) if (mUpdateOnResume) { - initFragments(mController.getTime(), mController.getViewType(), null); - mUpdateOnResume = false; - } - Time t = new Time(mTimeZone); - t.set(mController.getTime()); - mController.sendEvent(this, EventType.UPDATE_TITLE, t, t, -1, ViewType.CURRENT, - mController.getDateFlags(), null, null); + initFragments(mController.getTime(), mController.getViewType(), null) + mUpdateOnResume = false + } + val t = Time(mTimeZone) + t.set(mController.getTime()) + mController.sendEvent( + this, EventType.UPDATE_TITLE, t, t, -1, ViewType.CURRENT, + mController.getDateFlags(), null, null + ) // Make sure the drop-down menu will get its date updated at midnight if (mActionBarMenuSpinnerAdapter != null) { - mActionBarMenuSpinnerAdapter.refresh(this); + mActionBarMenuSpinnerAdapter.refresh(this) } - if (mControlsMenu != null) { - mControlsMenu.setTitle(mHideControls ? mShowString : mHideString); + mControlsMenu.setTitle(if (mHideControls) mShowString else mHideString) } - mPaused = false; - - if (mViewEventId != -1 && mIntentEventStartMillis != -1 && mIntentEventEndMillis != -1) { - long currentMillis = System.currentTimeMillis(); - long selectedTime = -1; + mPaused = false + if (mViewEventId != -1L && mIntentEventStartMillis != -1L && mIntentEventEndMillis != -1L) { + val currentMillis: Long = System.currentTimeMillis() + var selectedTime: Long = -1 if (currentMillis > mIntentEventStartMillis && currentMillis < mIntentEventEndMillis) { - selectedTime = currentMillis; + selectedTime = currentMillis } - mController.sendEventRelatedEventWithExtra(this, EventType.VIEW_EVENT, mViewEventId, - mIntentEventStartMillis, mIntentEventEndMillis, -1, -1, - EventInfo.buildViewExtraLong(mIntentAttendeeResponse,mIntentAllDay), - selectedTime); - mViewEventId = -1; - mIntentEventStartMillis = -1; - mIntentEventEndMillis = -1; - mIntentAllDay = false; - } - Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone); + mController.sendEventRelatedEventWithExtra( + this, EventType.VIEW_EVENT, mViewEventId, + mIntentEventStartMillis, mIntentEventEndMillis, -1, -1, + EventInfo.buildViewExtraLong(mIntentAttendeeResponse, mIntentAllDay), + selectedTime + ) + mViewEventId = -1 + mIntentEventStartMillis = -1 + mIntentEventEndMillis = -1 + mIntentAllDay = false + } + Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone) // Make sure the today icon is up to date - invalidateOptionsMenu(); + invalidateOptionsMenu() } @Override - protected void onPause() { - super.onPause(); - - mController.deregisterEventHandler(HANDLER_KEY); - mPaused = true; - mHomeTime.removeCallbacks(mHomeTimeUpdater); + protected fun onPause() { + super.onPause() + mController.deregisterEventHandler(HANDLER_KEY) + mPaused = true + mHomeTime.removeCallbacks(mHomeTimeUpdater) if (mActionBarMenuSpinnerAdapter != null) { - mActionBarMenuSpinnerAdapter.onPause(); + mActionBarMenuSpinnerAdapter.onPause() } - mContentResolver.unregisterContentObserver(mObserver); + mContentResolver.unregisterContentObserver(mObserver) if (isFinishing()) { // Stop listening for changes that would require this to be refreshed - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); - prefs.unregisterOnSharedPreferenceChangeListener(this); + val prefs: SharedPreferences = GeneralPreferences.getSharedPreferences(this) + prefs.unregisterOnSharedPreferenceChangeListener(this) } // FRAG_TODO save highlighted days of the week; - if (mController.getViewType() != ViewType.EDIT) { - Utils.setDefaultView(this, mController.getViewType()); + if (mController.getViewType() !== ViewType.EDIT) { + Utils.setDefaultView(this, mController.getViewType()) } - Utils.resetMidnightUpdater(mHandler, mTimeChangesUpdater); + Utils.resetMidnightUpdater(mHandler, mTimeChangesUpdater) } @Override - protected void onUserLeaveHint() { - mController.sendEvent(this, EventType.USER_HOME, null, null, -1, ViewType.CURRENT); - super.onUserLeaveHint(); + protected fun onUserLeaveHint() { + mController.sendEvent(this, EventType.USER_HOME, null, null, -1, ViewType.CURRENT) + super.onUserLeaveHint() } @Override - public void onSaveInstanceState(Bundle outState) { - mOnSaveInstanceStateCalled = true; - super.onSaveInstanceState(outState); + fun onSaveInstanceState(outState: Bundle?) { + mOnSaveInstanceStateCalled = true + super.onSaveInstanceState(outState) } @Override - protected void onDestroy() { - super.onDestroy(); - - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); - prefs.unregisterOnSharedPreferenceChangeListener(this); - - mController.deregisterAllEventHandlers(); - - CalendarController.removeInstance(this); + protected fun onDestroy() { + super.onDestroy() + val prefs: SharedPreferences = GeneralPreferences.getSharedPreferences(this) + prefs.unregisterOnSharedPreferenceChangeListener(this) + mController.deregisterAllEventHandlers() + CalendarController.removeInstance(this) } - private void initFragments(long timeMillis, int viewType, Bundle icicle) { + private fun initFragments(timeMillis: Long, viewType: Int, icicle: Bundle?) { if (DEBUG) { - Log.d(TAG, "Initializing to " + timeMillis + " for view " + viewType); + Log.d(TAG, "Initializing to $timeMillis for view $viewType") } - FragmentTransaction ft = getFragmentManager().beginTransaction(); - + val ft: FragmentTransaction = getFragmentManager().beginTransaction() if (mShowCalendarControls) { - Fragment miniMonthFrag = new MonthByWeekFragment(timeMillis, true); - ft.replace(R.id.mini_month, miniMonthFrag); - mController.registerEventHandler(R.id.mini_month, (EventHandler) miniMonthFrag); + val miniMonthFrag: Fragment = MonthByWeekFragment(timeMillis, true) + ft.replace(R.id.mini_month, miniMonthFrag) + mController.registerEventHandler(R.id.mini_month, miniMonthFrag as EventHandler) } if (!mShowCalendarControls || viewType == ViewType.EDIT) { - mMiniMonth.setVisibility(View.GONE); - mCalendarsList.setVisibility(View.GONE); + mMiniMonth.setVisibility(View.GONE) + mCalendarsList.setVisibility(View.GONE) } - - EventInfo info = null; + var info: EventInfo? = null if (viewType == ViewType.EDIT) { mPreviousView = GeneralPreferences.getSharedPreferences(this).getInt( - GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW); - - long eventId = -1; - Intent intent = getIntent(); - Uri data = intent.getData(); + GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW + ) + var eventId: Long = -1 + val intent: Intent = getIntent() + val data: Uri = intent.getData() if (data != null) { try { - eventId = Long.parseLong(data.getLastPathSegment()); - } catch (NumberFormatException e) { + eventId = Long.parseLong(data.getLastPathSegment()) + } catch (e: NumberFormatException) { if (DEBUG) { - Log.d(TAG, "Create new event"); + Log.d(TAG, "Create new event") } } } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) { - eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID); + eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID) } - - long begin = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1); - long end = intent.getLongExtra(EXTRA_EVENT_END_TIME, -1); - info = new EventInfo(); - if (end != -1) { - info.endTime = new Time(); - info.endTime.set(end); + val begin: Long = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1) + val end: Long = intent.getLongExtra(EXTRA_EVENT_END_TIME, -1) + info = EventInfo() + if (end != -1L) { + info.endTime = Time() + info.endTime.set(end) } - if (begin != -1) { - info.startTime = new Time(); - info.startTime.set(begin); + if (begin != -1L) { + info.startTime = Time() + info.startTime.set(begin) } - info.id = eventId; + info.id = eventId // We set the viewtype so if the user presses back when they are // done editing the controller knows we were in the Edit Event // screen. Likewise for eventId - mController.setViewType(viewType); - mController.setEventId(eventId); + mController.setViewType(viewType) + mController.setEventId(eventId) } else { - mPreviousView = viewType; + mPreviousView = viewType } - - setMainPane(ft, R.id.main_pane, viewType, timeMillis, true); - ft.commit(); // this needs to be after setMainPane() - - Time t = new Time(mTimeZone); - t.set(timeMillis); + setMainPane(ft, R.id.main_pane, viewType, timeMillis, true) + ft.commit() // this needs to be after setMainPane() + val t = Time(mTimeZone) + t.set(timeMillis) if (viewType != ViewType.EDIT) { - mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType); + mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType) } } @Override - public void onBackPressed() { + fun onBackPressed() { if (mCurrentView == ViewType.EDIT || mBackToPreviousView) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, mPreviousView); + mController.sendEvent(this, EventType.GO_TO, null, null, -1, mPreviousView) } else { - super.onBackPressed(); + super.onBackPressed() } } @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - mOptionsMenu = menu; - getMenuInflater().inflate(R.menu.all_in_one_title_bar, menu); + fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + mOptionsMenu = menu + getMenuInflater().inflate(R.menu.all_in_one_title_bar, menu) // Hide the "show/hide controls" button if this is a phone // or the view type is "Month". - - mControlsMenu = menu.findItem(R.id.action_hide_controls); + mControlsMenu = menu.findItem(R.id.action_hide_controls) if (!mShowCalendarControls) { if (mControlsMenu != null) { - mControlsMenu.setVisible(false); - mControlsMenu.setEnabled(false); + mControlsMenu.setVisible(false) + mControlsMenu.setEnabled(false) } - } else if (mControlsMenu != null && mController != null - && (mController.getViewType() == ViewType.MONTH)) { - mControlsMenu.setVisible(false); - mControlsMenu.setEnabled(false); - } else if (mControlsMenu != null){ - mControlsMenu.setTitle(mHideControls ? mShowString : mHideString); + } else if (mControlsMenu != null && mController != null && mController.getViewType() === ViewType.MONTH) { + mControlsMenu.setVisible(false) + mControlsMenu.setEnabled(false) + } else if (mControlsMenu != null) { + mControlsMenu.setTitle(if (mHideControls) mShowString else mHideString) } - - MenuItem menuItem = menu.findItem(R.id.action_today); + val menuItem: MenuItem = menu.findItem(R.id.action_today) if (Utils.isJellybeanOrLater()) { // replace the default top layer drawable of the today icon with a // custom drawable that shows the day of the month of today - LayerDrawable icon = (LayerDrawable) menuItem.getIcon(); - Utils.setTodayIcon(icon, this, mTimeZone); + val icon: LayerDrawable = menuItem.getIcon() as LayerDrawable + Utils.setTodayIcon(icon, this, mTimeZone) } else { - menuItem.setIcon(R.drawable.ic_menu_today_no_date_holo_light); + menuItem.setIcon(R.drawable.ic_menu_today_no_date_holo_light) } - return true; + return true } @Override - public boolean onOptionsItemSelected(MenuItem item) { - Time t = null; - int viewType = ViewType.CURRENT; - long extras = CalendarController.EXTRA_GOTO_TIME; - final int itemId = item.getItemId(); + fun onOptionsItemSelected(item: MenuItem): Boolean { + var t: Time? = null + var viewType: Int = ViewType.CURRENT + var extras: Long = CalendarController.EXTRA_GOTO_TIME + val itemId: Int = item.getItemId() if (itemId == R.id.action_today) { - viewType = ViewType.CURRENT; - t = new Time(mTimeZone); - t.setToNow(); - extras |= CalendarController.EXTRA_GOTO_TODAY; + viewType = ViewType.CURRENT + t = Time(mTimeZone) + t.setToNow() + extras = extras or CalendarController.EXTRA_GOTO_TODAY } else if (itemId == R.id.action_hide_controls) { - mHideControls = !mHideControls; - item.setTitle(mHideControls ? mShowString : mHideString); + mHideControls = !mHideControls + item.setTitle(if (mHideControls) mShowString else mHideString) if (!mHideControls) { - mMiniMonth.setVisibility(View.VISIBLE); - mCalendarsList.setVisibility(View.VISIBLE); - mMiniMonthContainer.setVisibility(View.VISIBLE); + mMiniMonth.setVisibility(View.VISIBLE) + mCalendarsList.setVisibility(View.VISIBLE) + mMiniMonthContainer.setVisibility(View.VISIBLE) } - final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this, "controlsOffset", - mHideControls ? 0 : mControlsAnimateWidth, - mHideControls ? mControlsAnimateWidth : 0); - slideAnimation.setDuration(mCalendarControlsAnimationTime); - ObjectAnimator.setFrameDelay(0); - slideAnimation.start(); - return true; + val slideAnimation: ObjectAnimator = ObjectAnimator.ofInt( + this, "controlsOffset", + if (mHideControls) 0 else mControlsAnimateWidth, + if (mHideControls) mControlsAnimateWidth else 0 + ) + slideAnimation.setDuration(mCalendarControlsAnimationTime) + ObjectAnimator.setFrameDelay(0) + slideAnimation.start() + return true } else { - Log.d(TAG, "Unsupported itemId: " + itemId); - return true; + Log.d(TAG, "Unsupported itemId: $itemId") + return true } - mController.sendEvent(this, EventType.GO_TO, t, null, t, -1, viewType, extras, null, null); - return true; + mController.sendEvent(this, EventType.GO_TO, t, null, t, -1, viewType, extras, null, null) + return true } /** @@ -712,351 +607,388 @@ public class AllInOneActivity extends Activity implements EventHandler, * * @param controlsOffset The current offset in pixels */ - public void setControlsOffset(int controlsOffset) { + fun setControlsOffset(controlsOffset: Int) { if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) { - mMiniMonth.setTranslationX(controlsOffset); - mCalendarsList.setTranslationX(controlsOffset); - mControlsParams.width = Math.max(0, mControlsAnimateWidth - controlsOffset); - mMiniMonthContainer.setLayoutParams(mControlsParams); + mMiniMonth.setTranslationX(controlsOffset) + mCalendarsList.setTranslationX(controlsOffset) + mControlsParams.width = Math.max(0, mControlsAnimateWidth - controlsOffset) + mMiniMonthContainer.setLayoutParams(mControlsParams) } else { - mMiniMonth.setTranslationY(controlsOffset); - mCalendarsList.setTranslationY(controlsOffset); + mMiniMonth.setTranslationY(controlsOffset) + mCalendarsList.setTranslationY(controlsOffset) if (mVerticalControlsParams == null) { - mVerticalControlsParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, mControlsAnimateHeight); + mVerticalControlsParams = LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, mControlsAnimateHeight + ) } - mVerticalControlsParams.height = Math.max(0, mControlsAnimateHeight - controlsOffset); - mMiniMonthContainer.setLayoutParams(mVerticalControlsParams); + mVerticalControlsParams.height = Math.max(0, mControlsAnimateHeight - controlsOffset) + mMiniMonthContainer.setLayoutParams(mVerticalControlsParams) } } @Override - public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + fun onSharedPreferenceChanged(prefs: SharedPreferences?, key: String) { if (key.equals(GeneralPreferences.KEY_WEEK_START_DAY)) { if (mPaused) { - mUpdateOnResume = true; + mUpdateOnResume = true } else { - initFragments(mController.getTime(), mController.getViewType(), null); + initFragments(mController.getTime(), mController.getViewType(), null) } } } - private void setMainPane( - FragmentTransaction ft, int viewId, int viewType, long timeMillis, boolean force) { + private fun setMainPane( + ft: FragmentTransaction?, viewId: Int, viewType: Int, timeMillis: Long, force: Boolean + ) { + var ft: FragmentTransaction? = ft if (mOnSaveInstanceStateCalled) { - return; + return } if (!force && mCurrentView == viewType) { - return; + return } // Remove this when transition to and from month view looks fine. - boolean doTransition = viewType != ViewType.MONTH && mCurrentView != ViewType.MONTH; - FragmentManager fragmentManager = getFragmentManager(); - + val doTransition = viewType != ViewType.MONTH && mCurrentView != ViewType.MONTH + val fragmentManager: FragmentManager = getFragmentManager() if (viewType != mCurrentView) { // The rules for this previous view are different than the // controller's and are used for intercepting the back button. if (mCurrentView != ViewType.EDIT && mCurrentView > 0) { - mPreviousView = mCurrentView; + mPreviousView = mCurrentView } - mCurrentView = viewType; + mCurrentView = viewType } // Create new fragment - Fragment frag = null; - Fragment secFrag = null; - switch (viewType) { - case ViewType.AGENDA: - break; - case ViewType.DAY: - if (mActionBar != null && (mActionBar.getSelectedTab() != mDayTab)) { - mActionBar.selectTab(mDayTab); + var frag: Fragment? = null + val secFrag: Fragment? = null + when (viewType) { + ViewType.AGENDA -> { + } + ViewType.DAY -> { + if (mActionBar != null && mActionBar.getSelectedTab() !== mDayTab) { + mActionBar.selectTab(mDayTab) } if (mActionBarMenuSpinnerAdapter != null) { - mActionBar.setSelectedNavigationItem(CalendarViewAdapter.DAY_BUTTON_INDEX); + mActionBar.setSelectedNavigationItem(CalendarViewAdapter.DAY_BUTTON_INDEX) } - frag = new DayFragment(timeMillis, 1); - break; - case ViewType.MONTH: - if (mActionBar != null && (mActionBar.getSelectedTab() != mMonthTab)) { - mActionBar.selectTab(mMonthTab); + frag = DayFragment(timeMillis, 1) + } + ViewType.MONTH -> { + if (mActionBar != null && mActionBar.getSelectedTab() !== mMonthTab) { + mActionBar.selectTab(mMonthTab) } if (mActionBarMenuSpinnerAdapter != null) { - mActionBar.setSelectedNavigationItem(CalendarViewAdapter.MONTH_BUTTON_INDEX); + mActionBar.setSelectedNavigationItem(CalendarViewAdapter.MONTH_BUTTON_INDEX) } - frag = new MonthByWeekFragment(timeMillis, false); - break; - case ViewType.WEEK: - default: - if (mActionBar != null && (mActionBar.getSelectedTab() != mWeekTab)) { - mActionBar.selectTab(mWeekTab); + frag = MonthByWeekFragment(timeMillis, false) + } + ViewType.WEEK -> { + if (mActionBar != null && mActionBar.getSelectedTab() !== mWeekTab) { + mActionBar.selectTab(mWeekTab) } if (mActionBarMenuSpinnerAdapter != null) { - mActionBar.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX); + mActionBar.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX) + } + frag = DayFragment(timeMillis, 7) + } + else -> { + if (mActionBar != null && mActionBar.getSelectedTab() !== mWeekTab) { + mActionBar.selectTab(mWeekTab) } - frag = new DayFragment(timeMillis, 7); - break; + if (mActionBarMenuSpinnerAdapter != null) { + mActionBar.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX) + } + frag = DayFragment(timeMillis, 7) + } } // Update the current view so that the menu can update its look according to the // current view. if (mActionBarMenuSpinnerAdapter != null) { - mActionBarMenuSpinnerAdapter.setMainView(viewType); + mActionBarMenuSpinnerAdapter.setMainView(viewType) if (!mIsTabletConfig) { - mActionBarMenuSpinnerAdapter.setTime(timeMillis); + mActionBarMenuSpinnerAdapter.setTime(timeMillis) } } // Show date only on tablet configurations in views different than Agenda if (!mIsTabletConfig) { - mDateRange.setVisibility(View.GONE); + mDateRange.setVisibility(View.GONE) } else { - mDateRange.setVisibility(View.GONE); + mDateRange.setVisibility(View.GONE) } // Clear unnecessary buttons from the option menu when switching from the agenda view if (viewType != ViewType.AGENDA) { - clearOptionsMenu(); + clearOptionsMenu() } - - boolean doCommit = false; + var doCommit = false if (ft == null) { - doCommit = true; - ft = fragmentManager.beginTransaction(); + doCommit = true + ft = fragmentManager.beginTransaction() } - if (doTransition) { - ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) } - - ft.replace(viewId, frag); + ft.replace(viewId, frag) if (DEBUG) { - Log.d(TAG, "Adding handler with viewId " + viewId + " and type " + viewType); + Log.d(TAG, "Adding handler with viewId $viewId and type $viewType") } // If the key is already registered this will replace it - mController.registerEventHandler(viewId, (EventHandler) frag); - + mController.registerEventHandler(viewId, frag as EventHandler?) if (doCommit) { if (DEBUG) { - Log.d(TAG, "setMainPane AllInOne=" + this + " finishing:" + this.isFinishing()); + Log.d(TAG, "setMainPane AllInOne=" + this + " finishing:" + this.isFinishing()) } - ft.commit(); + ft.commit() } } - private void setTitleInActionBar(EventInfo event) { - if (event.eventType != EventType.UPDATE_TITLE || mActionBar == null) { - return; + private fun setTitleInActionBar(event: EventInfo) { + if (event.eventType !== EventType.UPDATE_TITLE || mActionBar == null) { + return } - - final long start = event.startTime.toMillis(false /* use isDst */); - final long end; - if (event.endTime != null) { - end = event.endTime.toMillis(false /* use isDst */); + val start: Long = event.startTime.toMillis(false /* use isDst */) + val end: Long + end = if (event.endTime != null) { + event.endTime.toMillis(false /* use isDst */) } else { - end = start; + start } - - final String msg = Utils.formatDateRange(this, start, end, (int) event.extraLong); - CharSequence oldDate = mDateRange.getText(); - mDateRange.setText(msg); - updateSecondaryTitleFields(event.selectedTime != null ? event.selectedTime.toMillis(true) - : start); + val msg: String = Utils.formatDateRange(this, start, end, event.extraLong as Int) + val oldDate: CharSequence = mDateRange.getText() + mDateRange.setText(msg) + updateSecondaryTitleFields(if (event.selectedTime != null) event.selectedTime.toMillis(true) else start) if (!TextUtils.equals(oldDate, msg)) { - mDateRange.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + mDateRange.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) if (mShowWeekNum && mWeekTextView != null) { - mWeekTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + mWeekTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } } } - private void updateSecondaryTitleFields(long visibleMillisSinceEpoch) { - mShowWeekNum = Utils.getShowWeekNumber(this); - mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater); - if (visibleMillisSinceEpoch != -1) { - int weekNum = Utils.getWeekNumberFromTime(visibleMillisSinceEpoch, this); - mWeekNum = weekNum; - } - - if (mShowWeekNum && (mCurrentView == ViewType.WEEK) && mIsTabletConfig - && mWeekTextView != null) { - String weekString = getResources().getQuantityString(R.plurals.weekN, mWeekNum, - mWeekNum); - mWeekTextView.setText(weekString); - mWeekTextView.setVisibility(View.VISIBLE); - } else if (visibleMillisSinceEpoch != -1 && mWeekTextView != null - && mCurrentView == ViewType.DAY && mIsTabletConfig) { - Time time = new Time(mTimeZone); - time.set(visibleMillisSinceEpoch); - int julianDay = Time.getJulianDay(visibleMillisSinceEpoch, time.gmtoff); - time.setToNow(); - int todayJulianDay = Time.getJulianDay(time.toMillis(false), time.gmtoff); - String dayString = Utils.getDayOfWeekString(julianDay, todayJulianDay, - visibleMillisSinceEpoch, this); - mWeekTextView.setText(dayString); - mWeekTextView.setVisibility(View.VISIBLE); + private fun updateSecondaryTitleFields(visibleMillisSinceEpoch: Long) { + mShowWeekNum = Utils.getShowWeekNumber(this) + mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater) + if (visibleMillisSinceEpoch != -1L) { + val weekNum: Int = Utils.getWeekNumberFromTime(visibleMillisSinceEpoch, this) + mWeekNum = weekNum + } + if (mShowWeekNum && mCurrentView == ViewType.WEEK && mIsTabletConfig + && mWeekTextView != null + ) { + val weekString: String = getResources().getQuantityString( + R.plurals.weekN, mWeekNum, + mWeekNum + ) + mWeekTextView.setText(weekString) + mWeekTextView.setVisibility(View.VISIBLE) + } else if (visibleMillisSinceEpoch != -1L && mWeekTextView != null && mCurrentView == ViewType.DAY && mIsTabletConfig) { + val time = Time(mTimeZone) + time.set(visibleMillisSinceEpoch) + val julianDay: Int = Time.getJulianDay(visibleMillisSinceEpoch, time.gmtoff) + time.setToNow() + val todayJulianDay: Int = Time.getJulianDay(time.toMillis(false), time.gmtoff) + val dayString: String = Utils.getDayOfWeekString( + julianDay, + todayJulianDay, + visibleMillisSinceEpoch, + this + ) + mWeekTextView.setText(dayString) + mWeekTextView.setVisibility(View.VISIBLE) } else if (mWeekTextView != null && (!mIsTabletConfig || mCurrentView != ViewType.DAY)) { - mWeekTextView.setVisibility(View.GONE); - } - - if (mHomeTime != null - && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK) - && !TextUtils.equals(mTimeZone, Time.getCurrentTimezone())) { - Time time = new Time(mTimeZone); - time.setToNow(); - long millis = time.toMillis(true); - boolean isDST = time.isDst != 0; - int flags = DateUtils.FORMAT_SHOW_TIME; + mWeekTextView.setVisibility(View.GONE) + } + if (mHomeTime != null && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK) + && !TextUtils.equals(mTimeZone, Time.getCurrentTimezone()) + ) { + val time = Time(mTimeZone) + time.setToNow() + val millis: Long = time.toMillis(true) + val isDST = time.isDst !== 0 + var flags: Int = DateUtils.FORMAT_SHOW_TIME if (DateFormat.is24HourFormat(this)) { - flags |= DateUtils.FORMAT_24HOUR; + flags = flags or DateUtils.FORMAT_24HOUR } // Formats the time as - String timeString = (new StringBuilder( - Utils.formatDateRange(this, millis, millis, flags))).append(" ").append( - TimeZone.getTimeZone(mTimeZone).getDisplayName( - isDST, TimeZone.SHORT, Locale.getDefault())).toString(); - mHomeTime.setText(timeString); - mHomeTime.setVisibility(View.VISIBLE); + val timeString: String = StringBuilder( + Utils.formatDateRange(this, millis, millis, flags) + ).append(" ").append( + TimeZone.getTimeZone(mTimeZone).getDisplayName( + isDST, TimeZone.SHORT, Locale.getDefault() + ) + ).toString() + mHomeTime.setText(timeString) + mHomeTime.setVisibility(View.VISIBLE) // Update when the minute changes - mHomeTime.removeCallbacks(mHomeTimeUpdater); + mHomeTime.removeCallbacks(mHomeTimeUpdater) mHomeTime.postDelayed( - mHomeTimeUpdater, - DateUtils.MINUTE_IN_MILLIS - (millis % DateUtils.MINUTE_IN_MILLIS)); + mHomeTimeUpdater, + DateUtils.MINUTE_IN_MILLIS - millis % DateUtils.MINUTE_IN_MILLIS + ) } else if (mHomeTime != null) { - mHomeTime.setVisibility(View.GONE); + mHomeTime.setVisibility(View.GONE) } } - @Override - public long getSupportedEventTypes() { - return EventType.GO_TO | EventType.UPDATE_TITLE; - } + @get:Override val supportedEventTypes: Long + get() = EventType.GO_TO or EventType.UPDATE_TITLE @Override - public void handleEvent(EventInfo event) { - long displayTime = -1; - if (event.eventType == EventType.GO_TO) { - if ((event.extraLong & CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS) != 0) { - mBackToPreviousView = true; - } else if (event.viewType != mController.getPreviousViewType() - && event.viewType != ViewType.EDIT) { + fun handleEvent(event: EventInfo) { + var displayTime: Long = -1 + if (event.eventType === EventType.GO_TO) { + if (event.extraLong and CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS !== 0) { + mBackToPreviousView = true + } else if (event.viewType !== mController.getPreviousViewType() + && event.viewType !== ViewType.EDIT + ) { // Clear the flag is change to a different view type - mBackToPreviousView = false; + mBackToPreviousView = false } - setMainPane( - null, R.id.main_pane, event.viewType, event.startTime.toMillis(false), false); + null, R.id.main_pane, event.viewType, event.startTime.toMillis(false), false + ) if (mShowCalendarControls) { - int animationSize = (mOrientation == Configuration.ORIENTATION_LANDSCAPE) ? - mControlsAnimateWidth : mControlsAnimateHeight; - boolean noControlsView = event.viewType == ViewType.MONTH; + val animationSize = + if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) mControlsAnimateWidth else mControlsAnimateHeight + val noControlsView = event.viewType === ViewType.MONTH if (mControlsMenu != null) { - mControlsMenu.setVisible(!noControlsView); - mControlsMenu.setEnabled(!noControlsView); + mControlsMenu.setVisible(!noControlsView) + mControlsMenu.setEnabled(!noControlsView) } if (noControlsView || mHideControls) { // hide minimonth and calendar frag - mShowSideViews = false; + mShowSideViews = false if (!mHideControls) { - final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this, - "controlsOffset", 0, animationSize); - slideAnimation.addListener(mSlideAnimationDoneListener); - slideAnimation.setDuration(mCalendarControlsAnimationTime); - ObjectAnimator.setFrameDelay(0); - slideAnimation.start(); + val slideAnimation: ObjectAnimator = ObjectAnimator.ofInt( + this, + "controlsOffset", 0, animationSize + ) + slideAnimation.addListener(mSlideAnimationDoneListener) + slideAnimation.setDuration(mCalendarControlsAnimationTime) + ObjectAnimator.setFrameDelay(0) + slideAnimation.start() } else { - mMiniMonth.setVisibility(View.GONE); - mCalendarsList.setVisibility(View.GONE); - mMiniMonthContainer.setVisibility(View.GONE); + mMiniMonth.setVisibility(View.GONE) + mCalendarsList.setVisibility(View.GONE) + mMiniMonthContainer.setVisibility(View.GONE) } } else { // show minimonth and calendar frag - mShowSideViews = true; - mMiniMonth.setVisibility(View.VISIBLE); - mCalendarsList.setVisibility(View.VISIBLE); - mMiniMonthContainer.setVisibility(View.VISIBLE); + mShowSideViews = true + mMiniMonth.setVisibility(View.VISIBLE) + mCalendarsList.setVisibility(View.VISIBLE) + mMiniMonthContainer.setVisibility(View.VISIBLE) if (!mHideControls && - (mController.getPreviousViewType() == ViewType.MONTH)) { - final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this, - "controlsOffset", animationSize, 0); - slideAnimation.setDuration(mCalendarControlsAnimationTime); - ObjectAnimator.setFrameDelay(0); - slideAnimation.start(); + mController.getPreviousViewType() === ViewType.MONTH + ) { + val slideAnimation: ObjectAnimator = ObjectAnimator.ofInt( + this, + "controlsOffset", animationSize, 0 + ) + slideAnimation.setDuration(mCalendarControlsAnimationTime) + ObjectAnimator.setFrameDelay(0) + slideAnimation.start() } } } - displayTime = event.selectedTime != null ? event.selectedTime.toMillis(true) - : event.startTime.toMillis(true); + displayTime = + if (event.selectedTime != null) event.selectedTime.toMillis(true) else event.startTime.toMillis( + true + ) if (!mIsTabletConfig) { - mActionBarMenuSpinnerAdapter.setTime(displayTime); + mActionBarMenuSpinnerAdapter.setTime(displayTime) } - } else if (event.eventType == EventType.UPDATE_TITLE) { - setTitleInActionBar(event); + } else if (event.eventType === EventType.UPDATE_TITLE) { + setTitleInActionBar(event) if (!mIsTabletConfig) { - mActionBarMenuSpinnerAdapter.setTime(mController.getTime()); + mActionBarMenuSpinnerAdapter.setTime(mController.getTime()) } } - updateSecondaryTitleFields(displayTime); + updateSecondaryTitleFields(displayTime) } @Override - public void eventsChanged() { - mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT); + fun eventsChanged() { + mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT) } @Override - public void onTabSelected(Tab tab, FragmentTransaction ft) { - Log.w(TAG, "TabSelected AllInOne=" + this + " finishing:" + this.isFinishing()); - if (tab == mDayTab && mCurrentView != ViewType.DAY) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY); - } else if (tab == mWeekTab && mCurrentView != ViewType.WEEK) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK); - } else if (tab == mMonthTab && mCurrentView != ViewType.MONTH) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH); + fun onTabSelected(tab: Tab?, ft: FragmentTransaction?) { + Log.w(TAG, "TabSelected AllInOne=" + this + " finishing:" + this.isFinishing()) + if (tab === mDayTab && mCurrentView != ViewType.DAY) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY) + } else if (tab === mWeekTab && mCurrentView != ViewType.WEEK) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK) + } else if (tab === mMonthTab && mCurrentView != ViewType.MONTH) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH) } else { - Log.w(TAG, "TabSelected event from unknown tab: " - + (tab == null ? "null" : tab.getText())); - Log.w(TAG, "CurrentView:" + mCurrentView + " Tab:" + tab.toString() + " Day:" + mDayTab - + " Week:" + mWeekTab + " Month:" + mMonthTab); + Log.w( + TAG, "TabSelected event from unknown tab: " + + if (tab == null) "null" else tab.getText() + ) + Log.w( + TAG, "CurrentView:" + mCurrentView + " Tab:" + tab.toString() + " Day:" + mDayTab + + " Week:" + mWeekTab + " Month:" + mMonthTab + ) } } @Override - public void onTabReselected(Tab tab, FragmentTransaction ft) { + fun onTabReselected(tab: Tab?, ft: FragmentTransaction?) { } @Override - public void onTabUnselected(Tab tab, FragmentTransaction ft) { + fun onTabUnselected(tab: Tab?, ft: FragmentTransaction?) { } - @Override - public boolean onNavigationItemSelected(int itemPosition, long itemId) { - switch (itemPosition) { - case CalendarViewAdapter.DAY_BUTTON_INDEX: - if (mCurrentView != ViewType.DAY) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY); - } - break; - case CalendarViewAdapter.WEEK_BUTTON_INDEX: - if (mCurrentView != ViewType.WEEK) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK); - } - break; - case CalendarViewAdapter.MONTH_BUTTON_INDEX: - if (mCurrentView != ViewType.MONTH) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH); - } - break; - case CalendarViewAdapter.AGENDA_BUTTON_INDEX: - break; - default: - Log.w(TAG, "ItemSelected event from unknown button: " + itemPosition); - Log.w(TAG, "CurrentView:" + mCurrentView + " Button:" + itemPosition + - " Day:" + mDayTab + " Week:" + mWeekTab + " Month:" + mMonthTab); - break; + fun onNavigationItemSelected(itemPosition: Int, itemId: Long): Boolean { + when (itemPosition) { + CalendarViewAdapter.DAY_BUTTON_INDEX -> if (mCurrentView != ViewType.DAY) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY) + } + CalendarViewAdapter.WEEK_BUTTON_INDEX -> if (mCurrentView != ViewType.WEEK) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK) + } + CalendarViewAdapter.MONTH_BUTTON_INDEX -> if (mCurrentView != ViewType.MONTH) { + mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH) + } + CalendarViewAdapter.AGENDA_BUTTON_INDEX -> { + } + else -> { + Log.w(TAG, "ItemSelected event from unknown button: $itemPosition") + Log.w( + TAG, "CurrentView:" + mCurrentView + " Button:" + itemPosition + + " Day:" + mDayTab + " Week:" + mWeekTab + " Month:" + mMonthTab + ) + } } - return false; + return false + } + + companion object { + private const val TAG = "AllInOneActivity" + private const val DEBUG = false + private const val EVENT_INFO_FRAGMENT_TAG = "EventInfoFragment" + private const val BUNDLE_KEY_RESTORE_TIME = "key_restore_time" + private const val BUNDLE_KEY_EVENT_ID = "key_event_id" + private const val BUNDLE_KEY_RESTORE_VIEW = "key_restore_view" + private const val BUNDLE_KEY_CHECK_ACCOUNTS = "key_check_for_accounts" + private const val HANDLER_KEY = 0 + + // Indices of buttons for the drop down menu (tabs replacement) + // Must match the strings in the array buttons_list in arrays.xml and the + // OnNavigationListener + private const val BUTTON_DAY_INDEX = 0 + private const val BUTTON_WEEK_INDEX = 1 + private const val BUTTON_MONTH_INDEX = 2 + private const val BUTTON_AGENDA_INDEX = 3 + private var mIsMultipane = false + private var mIsTabletConfig = false } -} +} \ No newline at end of file -- cgit v1.2.3 From 1bbc1a051e0259b858958d9f392b093556002589 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Tue, 29 Jun 2021 22:14:31 +0000 Subject: AOSP/Calendar - AllInOneActivity fully converted with bp file This is the fully converted and touched-up Kotlin file after the automatic converter was run. I decided to tweak CalendarController as well so that Int is used in the place of the deprecated Integer type. The override modifier had to be added manually. Moreover, the converter did not make certain variables and parameters nullable which caused both compile and runtime errors. For instance, the parameter "Object" had to be changed to "Any?" in order to avoid a null pointer exception that caused the app to crash upon opening. Furthermore, I came the realization that casting from Float to Int cannot be done using the "as Int" expression; instead, invoking the ".toInt()" function is necessary to avoid casting exceptions. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.21 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 17m 53s Total aggregated tests run time: 17m 53s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.35 tests/sec [376 tests / 1073330 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 8936 ms || clean = 2335 ms Total preparation time: 8s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 19m 36s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: I83f47550fd74764e8d24bb55e54ca764d6bc808b --- src/com/android/calendar/AllInOneActivity.kt | 519 ++++++++++++++----------- src/com/android/calendar/CalendarController.kt | 142 +++---- 2 files changed, 366 insertions(+), 295 deletions(-) diff --git a/src/com/android/calendar/AllInOneActivity.kt b/src/com/android/calendar/AllInOneActivity.kt index d81f7b6d..1747bf58 100644 --- a/src/com/android/calendar/AllInOneActivity.kt +++ b/src/com/android/calendar/AllInOneActivity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -16,9 +16,61 @@ package com.android.calendar import android.accounts.AccountManager +import android.accounts.AccountManagerCallback +import android.accounts.AccountManagerFuture +import android.animation.Animator +import android.animation.Animator.AnimatorListener +import android.animation.ObjectAnimator +import android.app.ActionBar +import android.app.ActionBar.Tab +import android.app.Activity +import android.app.Fragment +import android.app.FragmentManager +import android.app.FragmentTransaction +import android.content.AsyncQueryHandler +import android.content.ContentResolver +import android.content.Intent +import android.content.SharedPreferences +import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import android.content.res.Configuration +import android.content.res.Resources +import android.database.ContentObserver +import android.database.Cursor +import android.graphics.drawable.LayerDrawable +import android.net.Uri +import android.os.Bundle +import android.os.Handler +import android.provider.CalendarContract +import android.provider.CalendarContract.Attendees +import android.provider.CalendarContract.Calendars +import android.provider.CalendarContract.Events +import android.text.TextUtils +import android.text.format.DateFormat +import android.text.format.DateUtils +import android.text.format.Time +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.accessibility.AccessibilityEvent +import android.widget.LinearLayout +import android.widget.RelativeLayout +import android.widget.RelativeLayout.LayoutParams +import android.widget.TextView +import com.android.calendar.CalendarController.EventHandler +import com.android.calendar.CalendarController.EventInfo +import com.android.calendar.CalendarController.EventType +import com.android.calendar.CalendarController.ViewType +import com.android.calendar.month.MonthByWeekFragment +import java.util.Locale +import java.util.TimeZone +import android.provider.CalendarContract.Attendees.ATTENDEE_STATUS +import android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY +import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME +import android.provider.CalendarContract.EXTRA_EVENT_END_TIME class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListener, - ActionBar.TabListener, ActionBar.OnNavigationListener { + ActionBar.TabListener, ActionBar.OnNavigationListener { private var mController: CalendarController? = null private var mOnSaveInstanceStateCalled = false private var mBackToPreviousView = false @@ -68,31 +120,31 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe // Params for animating the controls on the right private var mControlsParams: LayoutParams? = null private var mVerticalControlsParams: LinearLayout.LayoutParams? = null - private val mSlideAnimationDoneListener: AnimatorListener = object : AnimatorListener() { + private val mSlideAnimationDoneListener: AnimatorListener = object : AnimatorListener { @Override - fun onAnimationCancel(animation: Animator?) { + override fun onAnimationCancel(animation: Animator?) { } @Override - fun onAnimationEnd(animation: Animator?) { + override fun onAnimationEnd(animation: Animator?) { val visibility: Int = if (mShowSideViews) View.VISIBLE else View.GONE - mMiniMonth.setVisibility(visibility) - mCalendarsList.setVisibility(visibility) - mMiniMonthContainer.setVisibility(visibility) + mMiniMonth?.setVisibility(visibility) + mCalendarsList?.setVisibility(visibility) + mMiniMonthContainer?.setVisibility(visibility) } @Override - fun onAnimationRepeat(animation: Animator?) { + override fun onAnimationRepeat(animation: Animator?) { } @Override - fun onAnimationStart(animation: Animator?) { + override fun onAnimationStart(animation: Animator?) { } } private inner class QueryHandler(cr: ContentResolver?) : AsyncQueryHandler(cr) { @Override - protected fun onQueryComplete(token: Int, cookie: Object?, cursor: Cursor?) { + protected override fun onQueryComplete(token: Int, cookie: Any?, cursor: Cursor?) { mCheckForAccounts = false try { // If the query didn't return a cursor for some reason return @@ -112,19 +164,19 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe options.putBoolean("allowSkip", true) val am: AccountManager = AccountManager.get(this@AllInOneActivity) am.addAccount("com.google", CalendarContract.AUTHORITY, null, options, - this@AllInOneActivity, - object : AccountManagerCallback() { - @Override - fun run(future: AccountManagerFuture?) { - } - }, null + this@AllInOneActivity, + object : AccountManagerCallback { + @Override + override fun run(future: AccountManagerFuture?) { + } + }, null ) } } - private val mHomeTimeUpdater: Runnable = object : Runnable() { + private val mHomeTimeUpdater: Runnable = object : Runnable { @Override - fun run() { + override fun run() { mTimeZone = Utils.getTimeZone(this@AllInOneActivity, this) updateSecondaryTitleFields(-1) this@AllInOneActivity.invalidateOptionsMenu() @@ -133,9 +185,9 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe } // runs every midnight/time changes and refreshes the today icon - private val mTimeChangesUpdater: Runnable = object : Runnable() { + private val mTimeChangesUpdater: Runnable = object : Runnable { @Override - fun run() { + override fun run() { mTimeZone = Utils.getTimeZone(this@AllInOneActivity, mHomeTimeUpdater) this@AllInOneActivity.invalidateOptionsMenu() Utils.setMidnightUpdater(mHandler, this, mTimeZone) @@ -146,39 +198,40 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe // Calendar event changes. private val mObserver: ContentObserver = object : ContentObserver(Handler()) { @Override - fun deliverSelfNotifications(): Boolean { + override fun deliverSelfNotifications(): Boolean { return true } @Override - fun onChange(selfChange: Boolean) { + override fun onChange(selfChange: Boolean) { eventsChanged() } } @Override - protected fun onNewIntent(intent: Intent) { - val action: String = intent.getAction() + protected override fun onNewIntent(intent: Intent) { + val action: String? = intent.getAction() if (DEBUG) Log.d(TAG, "New intent received " + intent.toString()) // Don't change the date if we're just returning to the app's home - if (Intent.ACTION_VIEW.equals(action) - && !intent.getBooleanExtra(Utils.INTENT_KEY_HOME, false) + if (Intent.ACTION_VIEW.equals(action) && + !intent.getBooleanExtra(Utils.INTENT_KEY_HOME, false) ) { var millis = parseViewAction(intent) if (millis == -1L) { - millis = Utils.timeFromIntentInMillis(intent) + millis = Utils.timeFromIntentInMillis(intent) as Long } if (millis != -1L && mViewEventId == -1L && mController != null) { val time = Time(mTimeZone) time.set(millis) time.normalize(true) - mController.sendEvent(this, EventType.GO_TO, time, time, -1, ViewType.CURRENT) + mController?.sendEvent(this as Object?, EventType.GO_TO, time, time, -1, + ViewType.CURRENT) } } } @Override - protected fun onCreate(icicle: Bundle?) { + protected override fun onCreate(icicle: Bundle?) { super.onCreate(icicle) if (icicle != null && icicle.containsKey(BUNDLE_KEY_CHECK_ACCOUNTS)) { mCheckForAccounts = icicle.getBoolean(BUNDLE_KEY_CHECK_ACCOUNTS) @@ -187,7 +240,7 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe // accounts yet if (mCheckForAccounts) { mHandler = QueryHandler(this.getContentResolver()) - mHandler.startQuery( + mHandler?.startQuery( 0, null, Calendars.CONTENT_URI, arrayOf( Calendars._ID ), null, null /* selection args */, null /* sort order */ @@ -197,7 +250,6 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe // This needs to be created before setContentView mController = CalendarController.getInstance(this) - // Get time from intent or icicle var timeMillis: Long = -1 var viewType = -1 @@ -206,13 +258,13 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe timeMillis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME) viewType = icicle.getInt(BUNDLE_KEY_RESTORE_VIEW, -1) } else { - val action: String = intent.getAction() + val action: String? = intent.getAction() if (Intent.ACTION_VIEW.equals(action)) { // Open EventInfo later timeMillis = parseViewAction(intent) } if (timeMillis == -1L) { - timeMillis = Utils.timeFromIntentInMillis(intent) + timeMillis = Utils.timeFromIntentInMillis(intent) as Long } } if (viewType == -1 || viewType > ViewType.MAX_VALUE) { @@ -236,23 +288,24 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe mShowString = res.getString(R.string.show_controls) mOrientation = res.getConfiguration().orientation if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) { - mControlsAnimateWidth = res.getDimension(R.dimen.calendar_controls_width) + mControlsAnimateWidth = res.getDimension(R.dimen.calendar_controls_width).toInt() if (mControlsParams == null) { mControlsParams = LayoutParams(mControlsAnimateWidth, 0) } - mControlsParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT) + mControlsParams?.addRule(RelativeLayout.ALIGN_PARENT_RIGHT) + as RelativeLayout.LayoutParams } else { // Make sure width is in between allowed min and max width values mControlsAnimateWidth = Math.max( res.getDisplayMetrics().widthPixels * 45 / 100, - res.getDimension(R.dimen.min_portrait_calendar_controls_width) as Int + res.getDimension(R.dimen.min_portrait_calendar_controls_width).toInt() ) mControlsAnimateWidth = Math.min( mControlsAnimateWidth, - res.getDimension(R.dimen.max_portrait_calendar_controls_width) as Int + res.getDimension(R.dimen.max_portrait_calendar_controls_width).toInt() ) } - mControlsAnimateHeight = res.getDimension(R.dimen.calendar_controls_height) + mControlsAnimateHeight = res?.getDimension(R.dimen.calendar_controls_height).toInt() mHideControls = true mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config) mIsTabletConfig = Utils.getConfigBool(this, R.bool.tablet_config) @@ -277,7 +330,7 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe mHomeTime = findViewById(R.id.home_time) as TextView? mMiniMonth = findViewById(R.id.mini_month) if (mIsTabletConfig && mOrientation == Configuration.ORIENTATION_PORTRAIT) { - mMiniMonth.setLayoutParams( + mMiniMonth?.setLayoutParams( LayoutParams( mControlsAnimateWidth, mControlsAnimateHeight @@ -291,30 +344,31 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe // Must register as the first activity because this activity can modify // the list of event handlers in it's handle method. This affects who // the rest of the handlers the controller dispatches to are. - mController.registerFirstEventHandler(HANDLER_KEY, this) + mController?.registerFirstEventHandler(HANDLER_KEY, this) initFragments(timeMillis, viewType, icicle) // Listen for changes that would require this to be refreshed - val prefs: SharedPreferences = GeneralPreferences.getSharedPreferences(this) - prefs.registerOnSharedPreferenceChangeListener(this) + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(this) + prefs?.registerOnSharedPreferenceChangeListener(this) mContentResolver = getContentResolver() } private fun parseViewAction(intent: Intent?): Long { var timeMillis: Long = -1 - val data: Uri = intent.getData() - if (data != null && data.isHierarchical()) { - val path: List = data.getPathSegments() - if (path.size() === 2 && path[0].equals("events")) { + val data: Uri? = intent?.getData() + if (data != null && data?.isHierarchical()) { + val path = data.getPathSegments() + if (path?.size == 2 && path!![0].equals("events")) { try { - mViewEventId = Long.valueOf(data.getLastPathSegment()) + mViewEventId = data.getLastPathSegment()?.toLong() as Long if (mViewEventId != -1L) { - mIntentEventStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0) - mIntentEventEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0) - mIntentAttendeeResponse = intent.getIntExtra( + mIntentEventStartMillis = intent?.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0) + mIntentEventEndMillis = intent?.getLongExtra(EXTRA_EVENT_END_TIME, 0) + mIntentAttendeeResponse = intent?.getIntExtra( ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE ) - mIntentAllDay = intent.getBooleanExtra(EXTRA_EVENT_ALL_DAY, false) + mIntentAllDay = intent?.getBooleanExtra(EXTRA_EVENT_ALL_DAY, false) + as Boolean timeMillis = mIntentEventStartMillis } } catch (e: NumberFormatException) { @@ -328,11 +382,11 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe private fun configureActionBar(viewType: Int) { createButtonsSpinner(viewType, mIsTabletConfig) if (mIsMultipane) { - mActionBar.setDisplayOptions( + mActionBar?.setDisplayOptions( ActionBar.DISPLAY_SHOW_CUSTOM or ActionBar.DISPLAY_SHOW_HOME ) } else { - mActionBar.setDisplayOptions(0) + mActionBar?.setDisplayOptions(0) } } @@ -340,15 +394,15 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe // If tablet configuration , show spinner with no dates mActionBarMenuSpinnerAdapter = CalendarViewAdapter(this, viewType, !tabletConfig) mActionBar = getActionBar() - mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST) - mActionBar.setListNavigationCallbacks(mActionBarMenuSpinnerAdapter, this) + mActionBar?.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST) + mActionBar?.setListNavigationCallbacks(mActionBarMenuSpinnerAdapter, this) when (viewType) { ViewType.AGENDA -> { } - ViewType.DAY -> mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX) - ViewType.WEEK -> mActionBar.setSelectedNavigationItem(BUTTON_WEEK_INDEX) - ViewType.MONTH -> mActionBar.setSelectedNavigationItem(BUTTON_MONTH_INDEX) - else -> mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX) + ViewType.DAY -> mActionBar?.setSelectedNavigationItem(BUTTON_DAY_INDEX) + ViewType.WEEK -> mActionBar?.setSelectedNavigationItem(BUTTON_WEEK_INDEX) + ViewType.MONTH -> mActionBar?.setSelectedNavigationItem(BUTTON_MONTH_INDEX) + else -> mActionBar?.setSelectedNavigationItem(BUTTON_DAY_INDEX) } } @@ -357,14 +411,14 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe if (mOptionsMenu == null) { return } - val cancelItem: MenuItem = mOptionsMenu.findItem(R.id.action_cancel) + val cancelItem: MenuItem? = mOptionsMenu?.findItem(R.id.action_cancel) if (cancelItem != null) { - cancelItem.setVisible(false) + cancelItem?.setVisible(false) } } @Override - protected fun onResume() { + protected override fun onResume() { super.onResume() // Check if the upgrade code has ever been run. If not, force a sync just this one time. @@ -373,28 +427,28 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe // Must register as the first activity because this activity can modify // the list of event handlers in it's handle method. This affects who // the rest of the handlers the controller dispatches to are. - mController.registerFirstEventHandler(HANDLER_KEY, this) + mController?.registerFirstEventHandler(HANDLER_KEY, this) mOnSaveInstanceStateCalled = false - mContentResolver.registerContentObserver( + mContentResolver?.registerContentObserver( CalendarContract.Events.CONTENT_URI, true, mObserver ) if (mUpdateOnResume) { - initFragments(mController.getTime(), mController.getViewType(), null) + initFragments(mController?.time as Long, mController?.viewType as Int, null) mUpdateOnResume = false } val t = Time(mTimeZone) - t.set(mController.getTime()) - mController.sendEvent( - this, EventType.UPDATE_TITLE, t, t, -1, ViewType.CURRENT, - mController.getDateFlags(), null, null + t.set(mController?.time as Long) + mController?.sendEvent( + this as Object?, EventType.UPDATE_TITLE, t, t, -1, ViewType.CURRENT, + mController?.dateFlags as Long, null, null ) // Make sure the drop-down menu will get its date updated at midnight if (mActionBarMenuSpinnerAdapter != null) { - mActionBarMenuSpinnerAdapter.refresh(this) + mActionBarMenuSpinnerAdapter?.refresh(this) } if (mControlsMenu != null) { - mControlsMenu.setTitle(if (mHideControls) mShowString else mHideString) + mControlsMenu?.setTitle(if (mHideControls) mShowString else mHideString) } mPaused = false if (mViewEventId != -1L && mIntentEventStartMillis != -1L && mIntentEventEndMillis != -1L) { @@ -403,8 +457,8 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe if (currentMillis > mIntentEventStartMillis && currentMillis < mIntentEventEndMillis) { selectedTime = currentMillis } - mController.sendEventRelatedEventWithExtra( - this, EventType.VIEW_EVENT, mViewEventId, + mController?.sendEventRelatedEventWithExtra( + this as Object?, EventType.VIEW_EVENT, mViewEventId, mIntentEventStartMillis, mIntentEventEndMillis, -1, -1, EventInfo.buildViewExtraLong(mIntentAttendeeResponse, mIntentAllDay), selectedTime @@ -420,45 +474,46 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe } @Override - protected fun onPause() { + protected override fun onPause() { super.onPause() - mController.deregisterEventHandler(HANDLER_KEY) + mController?.deregisterEventHandler(HANDLER_KEY) mPaused = true - mHomeTime.removeCallbacks(mHomeTimeUpdater) + mHomeTime?.removeCallbacks(mHomeTimeUpdater) if (mActionBarMenuSpinnerAdapter != null) { - mActionBarMenuSpinnerAdapter.onPause() + mActionBarMenuSpinnerAdapter?.onPause() } - mContentResolver.unregisterContentObserver(mObserver) + mContentResolver?.unregisterContentObserver(mObserver) if (isFinishing()) { // Stop listening for changes that would require this to be refreshed - val prefs: SharedPreferences = GeneralPreferences.getSharedPreferences(this) - prefs.unregisterOnSharedPreferenceChangeListener(this) + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(this) + prefs?.unregisterOnSharedPreferenceChangeListener(this) } // FRAG_TODO save highlighted days of the week; - if (mController.getViewType() !== ViewType.EDIT) { - Utils.setDefaultView(this, mController.getViewType()) + if (mController?.viewType != ViewType.EDIT) { + Utils.setDefaultView(this, mController?.viewType as Int) } Utils.resetMidnightUpdater(mHandler, mTimeChangesUpdater) } @Override - protected fun onUserLeaveHint() { - mController.sendEvent(this, EventType.USER_HOME, null, null, -1, ViewType.CURRENT) + protected override fun onUserLeaveHint() { + mController?.sendEvent(this as Object?, EventType.USER_HOME, null, null, -1, + ViewType.CURRENT) super.onUserLeaveHint() } @Override - fun onSaveInstanceState(outState: Bundle?) { + override fun onSaveInstanceState(outState: Bundle) { mOnSaveInstanceStateCalled = true super.onSaveInstanceState(outState) } @Override - protected fun onDestroy() { + protected override fun onDestroy() { super.onDestroy() - val prefs: SharedPreferences = GeneralPreferences.getSharedPreferences(this) - prefs.unregisterOnSharedPreferenceChangeListener(this) - mController.deregisterAllEventHandlers() + val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(this) + prefs?.unregisterOnSharedPreferenceChangeListener(this) + mController?.deregisterAllEventHandlers() CalendarController.removeInstance(this) } @@ -470,23 +525,23 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe if (mShowCalendarControls) { val miniMonthFrag: Fragment = MonthByWeekFragment(timeMillis, true) ft.replace(R.id.mini_month, miniMonthFrag) - mController.registerEventHandler(R.id.mini_month, miniMonthFrag as EventHandler) + mController?.registerEventHandler(R.id.mini_month, miniMonthFrag as EventHandler) } if (!mShowCalendarControls || viewType == ViewType.EDIT) { - mMiniMonth.setVisibility(View.GONE) - mCalendarsList.setVisibility(View.GONE) + mMiniMonth?.setVisibility(View.GONE) + mCalendarsList?.setVisibility(View.GONE) } var info: EventInfo? = null if (viewType == ViewType.EDIT) { - mPreviousView = GeneralPreferences.getSharedPreferences(this).getInt( + mPreviousView = GeneralPreferences.getSharedPreferences(this)?.getInt( GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW - ) + ) as Int var eventId: Long = -1 val intent: Intent = getIntent() - val data: Uri = intent.getData() + val data: Uri? = intent.getData() if (data != null) { try { - eventId = Long.parseLong(data.getLastPathSegment()) + eventId = data?.getLastPathSegment()?.toLong() as Long } catch (e: NumberFormatException) { if (DEBUG) { Log.d(TAG, "Create new event") @@ -499,19 +554,19 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe val end: Long = intent.getLongExtra(EXTRA_EVENT_END_TIME, -1) info = EventInfo() if (end != -1L) { - info.endTime = Time() - info.endTime.set(end) + info?.endTime = Time() + info?.endTime?.set(end) } if (begin != -1L) { - info.startTime = Time() - info.startTime.set(begin) + info?.startTime = Time() + info?.startTime?.set(begin) } info.id = eventId // We set the viewtype so if the user presses back when they are // done editing the controller knows we were in the Edit Event // screen. Likewise for eventId - mController.setViewType(viewType) - mController.setEventId(eventId) + mController?.viewType = viewType + mController?.eventId = eventId } else { mPreviousView = viewType } @@ -520,21 +575,21 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe val t = Time(mTimeZone) t.set(timeMillis) if (viewType != ViewType.EDIT) { - mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType) + mController?.sendEvent(this as Object?, EventType.GO_TO, t, null, -1, viewType) } } @Override - fun onBackPressed() { + override fun onBackPressed() { if (mCurrentView == ViewType.EDIT || mBackToPreviousView) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, mPreviousView) + mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, mPreviousView) } else { super.onBackPressed() } } @Override - fun onCreateOptionsMenu(menu: Menu): Boolean { + override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) mOptionsMenu = menu getMenuInflater().inflate(R.menu.all_in_one_title_bar, menu) @@ -544,14 +599,15 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe mControlsMenu = menu.findItem(R.id.action_hide_controls) if (!mShowCalendarControls) { if (mControlsMenu != null) { - mControlsMenu.setVisible(false) - mControlsMenu.setEnabled(false) + mControlsMenu?.setVisible(false) + mControlsMenu?.setEnabled(false) } - } else if (mControlsMenu != null && mController != null && mController.getViewType() === ViewType.MONTH) { - mControlsMenu.setVisible(false) - mControlsMenu.setEnabled(false) + } else if (mControlsMenu != null && mController != null && + mController?.viewType == ViewType.MONTH) { + mControlsMenu?.setVisible(false) + mControlsMenu?.setEnabled(false) } else if (mControlsMenu != null) { - mControlsMenu.setTitle(if (mHideControls) mShowString else mHideString) + mControlsMenu?.setTitle(if (mHideControls) mShowString else mHideString) } val menuItem: MenuItem = menu.findItem(R.id.action_today) if (Utils.isJellybeanOrLater()) { @@ -566,7 +622,7 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe } @Override - fun onOptionsItemSelected(item: MenuItem): Boolean { + override fun onOptionsItemSelected(item: MenuItem): Boolean { var t: Time? = null var viewType: Int = ViewType.CURRENT var extras: Long = CalendarController.EXTRA_GOTO_TIME @@ -580,16 +636,16 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe mHideControls = !mHideControls item.setTitle(if (mHideControls) mShowString else mHideString) if (!mHideControls) { - mMiniMonth.setVisibility(View.VISIBLE) - mCalendarsList.setVisibility(View.VISIBLE) - mMiniMonthContainer.setVisibility(View.VISIBLE) + mMiniMonth?.setVisibility(View.VISIBLE) + mCalendarsList?.setVisibility(View.VISIBLE) + mMiniMonthContainer?.setVisibility(View.VISIBLE) } val slideAnimation: ObjectAnimator = ObjectAnimator.ofInt( this, "controlsOffset", if (mHideControls) 0 else mControlsAnimateWidth, if (mHideControls) mControlsAnimateWidth else 0 ) - slideAnimation.setDuration(mCalendarControlsAnimationTime) + slideAnimation.setDuration(mCalendarControlsAnimationTime.toLong()) ObjectAnimator.setFrameDelay(0) slideAnimation.start() return true @@ -597,7 +653,8 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe Log.d(TAG, "Unsupported itemId: $itemId") return true } - mController.sendEvent(this, EventType.GO_TO, t, null, t, -1, viewType, extras, null, null) + mController?.sendEvent(this as Object?, EventType.GO_TO, t, null, t, -1, + viewType, extras, null, null) return true } @@ -609,36 +666,40 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe */ fun setControlsOffset(controlsOffset: Int) { if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) { - mMiniMonth.setTranslationX(controlsOffset) - mCalendarsList.setTranslationX(controlsOffset) - mControlsParams.width = Math.max(0, mControlsAnimateWidth - controlsOffset) - mMiniMonthContainer.setLayoutParams(mControlsParams) + mMiniMonth?.setTranslationX(controlsOffset.toFloat()) + mCalendarsList?.setTranslationX(controlsOffset.toFloat()) + mControlsParams?.width = Math.max(0, mControlsAnimateWidth - controlsOffset) + mMiniMonthContainer?.setLayoutParams(mControlsParams) } else { - mMiniMonth.setTranslationY(controlsOffset) - mCalendarsList.setTranslationY(controlsOffset) + mMiniMonth?.setTranslationY(controlsOffset.toFloat()) + mCalendarsList?.setTranslationY(controlsOffset.toFloat()) if (mVerticalControlsParams == null) { mVerticalControlsParams = LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, mControlsAnimateHeight - ) + ) as LinearLayout.LayoutParams? } - mVerticalControlsParams.height = Math.max(0, mControlsAnimateHeight - controlsOffset) - mMiniMonthContainer.setLayoutParams(mVerticalControlsParams) + mVerticalControlsParams?.height = Math.max(0, mControlsAnimateHeight - controlsOffset) + mMiniMonthContainer?.setLayoutParams(mVerticalControlsParams) } } @Override - fun onSharedPreferenceChanged(prefs: SharedPreferences?, key: String) { + override fun onSharedPreferenceChanged(prefs: SharedPreferences?, key: String) { if (key.equals(GeneralPreferences.KEY_WEEK_START_DAY)) { if (mPaused) { mUpdateOnResume = true } else { - initFragments(mController.getTime(), mController.getViewType(), null) + initFragments(mController?.time as Long, mController?.viewType as Int, null) } } } private fun setMainPane( - ft: FragmentTransaction?, viewId: Int, viewType: Int, timeMillis: Long, force: Boolean + ft: FragmentTransaction?, + viewId: Int, + viewType: Int, + timeMillis: Long, + force: Boolean ) { var ft: FragmentTransaction? = ft if (mOnSaveInstanceStateCalled) { @@ -666,38 +727,38 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe ViewType.AGENDA -> { } ViewType.DAY -> { - if (mActionBar != null && mActionBar.getSelectedTab() !== mDayTab) { - mActionBar.selectTab(mDayTab) + if (mActionBar != null && mActionBar?.getSelectedTab() != mDayTab) { + mActionBar?.selectTab(mDayTab) } if (mActionBarMenuSpinnerAdapter != null) { - mActionBar.setSelectedNavigationItem(CalendarViewAdapter.DAY_BUTTON_INDEX) + mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.DAY_BUTTON_INDEX) } frag = DayFragment(timeMillis, 1) } ViewType.MONTH -> { - if (mActionBar != null && mActionBar.getSelectedTab() !== mMonthTab) { - mActionBar.selectTab(mMonthTab) + if (mActionBar != null && mActionBar?.getSelectedTab() != mMonthTab) { + mActionBar?.selectTab(mMonthTab) } if (mActionBarMenuSpinnerAdapter != null) { - mActionBar.setSelectedNavigationItem(CalendarViewAdapter.MONTH_BUTTON_INDEX) + mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.MONTH_BUTTON_INDEX) } frag = MonthByWeekFragment(timeMillis, false) } ViewType.WEEK -> { - if (mActionBar != null && mActionBar.getSelectedTab() !== mWeekTab) { - mActionBar.selectTab(mWeekTab) + if (mActionBar != null && mActionBar?.getSelectedTab() != mWeekTab) { + mActionBar?.selectTab(mWeekTab) } if (mActionBarMenuSpinnerAdapter != null) { - mActionBar.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX) + mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX) } frag = DayFragment(timeMillis, 7) } else -> { - if (mActionBar != null && mActionBar.getSelectedTab() !== mWeekTab) { - mActionBar.selectTab(mWeekTab) + if (mActionBar != null && mActionBar?.getSelectedTab() != mWeekTab) { + mActionBar?.selectTab(mWeekTab) } if (mActionBarMenuSpinnerAdapter != null) { - mActionBar.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX) + mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX) } frag = DayFragment(timeMillis, 7) } @@ -706,18 +767,17 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe // Update the current view so that the menu can update its look according to the // current view. if (mActionBarMenuSpinnerAdapter != null) { - mActionBarMenuSpinnerAdapter.setMainView(viewType) + mActionBarMenuSpinnerAdapter?.setMainView(viewType) if (!mIsTabletConfig) { - mActionBarMenuSpinnerAdapter.setTime(timeMillis) + mActionBarMenuSpinnerAdapter?.setTime(timeMillis) } } - // Show date only on tablet configurations in views different than Agenda if (!mIsTabletConfig) { - mDateRange.setVisibility(View.GONE) + mDateRange?.setVisibility(View.GONE) } else { - mDateRange.setVisibility(View.GONE) + mDateRange?.setVisibility(View.GONE) } // Clear unnecessary buttons from the option menu when switching from the agenda view @@ -730,41 +790,46 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe ft = fragmentManager.beginTransaction() } if (doTransition) { - ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) + ft?.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) } - ft.replace(viewId, frag) + ft?.replace(viewId, frag) if (DEBUG) { Log.d(TAG, "Adding handler with viewId $viewId and type $viewType") } // If the key is already registered this will replace it - mController.registerEventHandler(viewId, frag as EventHandler?) + mController?.registerEventHandler(viewId, frag as EventHandler?) if (doCommit) { if (DEBUG) { Log.d(TAG, "setMainPane AllInOne=" + this + " finishing:" + this.isFinishing()) } - ft.commit() + ft?.commit() } } private fun setTitleInActionBar(event: EventInfo) { - if (event.eventType !== EventType.UPDATE_TITLE || mActionBar == null) { + if (event.eventType != EventType.UPDATE_TITLE || mActionBar == null) { return } - val start: Long = event.startTime.toMillis(false /* use isDst */) - val end: Long + val start: Long? = event?.startTime?.toMillis(false /* use isDst */) + val end: Long? end = if (event.endTime != null) { - event.endTime.toMillis(false /* use isDst */) + event?.endTime?.toMillis(false /* use isDst */) } else { start } - val msg: String = Utils.formatDateRange(this, start, end, event.extraLong as Int) - val oldDate: CharSequence = mDateRange.getText() - mDateRange.setText(msg) - updateSecondaryTitleFields(if (event.selectedTime != null) event.selectedTime.toMillis(true) else start) + val msg: String? = Utils.formatDateRange(this, + start as Long, + end as Long, + event.extraLong.toInt() + ) + val oldDate: CharSequence? = mDateRange?.getText() + mDateRange?.setText(msg) + updateSecondaryTitleFields(if (event?.selectedTime != null) + event?.selectedTime?.toMillis(true) as Long else start) if (!TextUtils.equals(oldDate, msg)) { - mDateRange.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + mDateRange?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) if (mShowWeekNum && mWeekTextView != null) { - mWeekTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + mWeekTextView?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } } } @@ -776,16 +841,17 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe val weekNum: Int = Utils.getWeekNumberFromTime(visibleMillisSinceEpoch, this) mWeekNum = weekNum } - if (mShowWeekNum && mCurrentView == ViewType.WEEK && mIsTabletConfig - && mWeekTextView != null + if (mShowWeekNum && mCurrentView == ViewType.WEEK && mIsTabletConfig && + mWeekTextView != null ) { val weekString: String = getResources().getQuantityString( R.plurals.weekN, mWeekNum, mWeekNum ) - mWeekTextView.setText(weekString) - mWeekTextView.setVisibility(View.VISIBLE) - } else if (visibleMillisSinceEpoch != -1L && mWeekTextView != null && mCurrentView == ViewType.DAY && mIsTabletConfig) { + mWeekTextView?.setText(weekString) + mWeekTextView?.setVisibility(View.VISIBLE) + } else if (visibleMillisSinceEpoch != -1L && mWeekTextView != null && + mCurrentView == ViewType.DAY && mIsTabletConfig) { val time = Time(mTimeZone) time.set(visibleMillisSinceEpoch) val julianDay: Int = Time.getJulianDay(visibleMillisSinceEpoch, time.gmtoff) @@ -797,13 +863,13 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe visibleMillisSinceEpoch, this ) - mWeekTextView.setText(dayString) - mWeekTextView.setVisibility(View.VISIBLE) + mWeekTextView?.setText(dayString) + mWeekTextView?.setVisibility(View.VISIBLE) } else if (mWeekTextView != null && (!mIsTabletConfig || mCurrentView != ViewType.DAY)) { - mWeekTextView.setVisibility(View.GONE) + mWeekTextView?.setVisibility(View.GONE) } - if (mHomeTime != null && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK) - && !TextUtils.equals(mTimeZone, Time.getCurrentTimezone()) + if (mHomeTime != null && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK) && + !TextUtils.equals(mTimeZone, Time.getCurrentTimezone()) ) { val time = Time(mTimeZone) time.setToNow() @@ -821,44 +887,46 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe isDST, TimeZone.SHORT, Locale.getDefault() ) ).toString() - mHomeTime.setText(timeString) - mHomeTime.setVisibility(View.VISIBLE) + mHomeTime?.setText(timeString) + mHomeTime?.setVisibility(View.VISIBLE) // Update when the minute changes - mHomeTime.removeCallbacks(mHomeTimeUpdater) - mHomeTime.postDelayed( + mHomeTime?.removeCallbacks(mHomeTimeUpdater) + mHomeTime?.postDelayed( mHomeTimeUpdater, DateUtils.MINUTE_IN_MILLIS - millis % DateUtils.MINUTE_IN_MILLIS ) } else if (mHomeTime != null) { - mHomeTime.setVisibility(View.GONE) + mHomeTime?.setVisibility(View.GONE) } } - @get:Override val supportedEventTypes: Long + @get:Override override val supportedEventTypes: Long get() = EventType.GO_TO or EventType.UPDATE_TITLE @Override - fun handleEvent(event: EventInfo) { + override fun handleEvent(event: EventInfo?) { var displayTime: Long = -1 - if (event.eventType === EventType.GO_TO) { - if (event.extraLong and CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS !== 0) { + if (event?.eventType == EventType.GO_TO) { + if (event?.extraLong and CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS != 0L) { mBackToPreviousView = true - } else if (event.viewType !== mController.getPreviousViewType() - && event.viewType !== ViewType.EDIT + } else if (event?.viewType != mController?.previousViewType && + event?.viewType != ViewType.EDIT ) { // Clear the flag is change to a different view type mBackToPreviousView = false } setMainPane( - null, R.id.main_pane, event.viewType, event.startTime.toMillis(false), false + null, R.id.main_pane, event?.viewType, event?.startTime?.toMillis(false) + as Long, false ) if (mShowCalendarControls) { val animationSize = - if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) mControlsAnimateWidth else mControlsAnimateHeight - val noControlsView = event.viewType === ViewType.MONTH + if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) mControlsAnimateWidth + else mControlsAnimateHeight + val noControlsView = event?.viewType == ViewType.MONTH if (mControlsMenu != null) { - mControlsMenu.setVisible(!noControlsView) - mControlsMenu.setEnabled(!noControlsView) + mControlsMenu?.setVisible(!noControlsView) + mControlsMenu?.setEnabled(!noControlsView) } if (noControlsView || mHideControls) { // hide minimonth and calendar frag @@ -869,94 +937,97 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe "controlsOffset", 0, animationSize ) slideAnimation.addListener(mSlideAnimationDoneListener) - slideAnimation.setDuration(mCalendarControlsAnimationTime) + slideAnimation.setDuration(mCalendarControlsAnimationTime.toLong()) ObjectAnimator.setFrameDelay(0) slideAnimation.start() } else { - mMiniMonth.setVisibility(View.GONE) - mCalendarsList.setVisibility(View.GONE) - mMiniMonthContainer.setVisibility(View.GONE) + mMiniMonth?.setVisibility(View.GONE) + mCalendarsList?.setVisibility(View.GONE) + mMiniMonthContainer?.setVisibility(View.GONE) } } else { // show minimonth and calendar frag mShowSideViews = true - mMiniMonth.setVisibility(View.VISIBLE) - mCalendarsList.setVisibility(View.VISIBLE) - mMiniMonthContainer.setVisibility(View.VISIBLE) + mMiniMonth?.setVisibility(View.VISIBLE) + mCalendarsList?.setVisibility(View.VISIBLE) + mMiniMonthContainer?.setVisibility(View.VISIBLE) if (!mHideControls && - mController.getPreviousViewType() === ViewType.MONTH + mController?.previousViewType == ViewType.MONTH ) { val slideAnimation: ObjectAnimator = ObjectAnimator.ofInt( this, "controlsOffset", animationSize, 0 ) - slideAnimation.setDuration(mCalendarControlsAnimationTime) + slideAnimation.setDuration(mCalendarControlsAnimationTime.toLong()) ObjectAnimator.setFrameDelay(0) slideAnimation.start() } } } displayTime = - if (event.selectedTime != null) event.selectedTime.toMillis(true) else event.startTime.toMillis( - true - ) + if (event?.selectedTime != null) event?.selectedTime?.toMillis(true) as Long + else event?.startTime?.toMillis(true) as Long if (!mIsTabletConfig) { - mActionBarMenuSpinnerAdapter.setTime(displayTime) + mActionBarMenuSpinnerAdapter?.setTime(displayTime) } - } else if (event.eventType === EventType.UPDATE_TITLE) { - setTitleInActionBar(event) + } else if (event?.eventType == EventType.UPDATE_TITLE) { + setTitleInActionBar(event as CalendarController.EventInfo) if (!mIsTabletConfig) { - mActionBarMenuSpinnerAdapter.setTime(mController.getTime()) + mActionBarMenuSpinnerAdapter?.setTime(mController?.time as Long) } } updateSecondaryTitleFields(displayTime) } @Override - fun eventsChanged() { - mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT) + override fun eventsChanged() { + mController?.sendEvent(this as Object?, EventType.EVENTS_CHANGED, null, null, -1, + ViewType.CURRENT) } @Override - fun onTabSelected(tab: Tab?, ft: FragmentTransaction?) { + override fun onTabSelected(tab: Tab?, ft: FragmentTransaction?) { Log.w(TAG, "TabSelected AllInOne=" + this + " finishing:" + this.isFinishing()) - if (tab === mDayTab && mCurrentView != ViewType.DAY) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY) - } else if (tab === mWeekTab && mCurrentView != ViewType.WEEK) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK) - } else if (tab === mMonthTab && mCurrentView != ViewType.MONTH) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH) + if (tab == mDayTab && mCurrentView != ViewType.DAY) { + mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, ViewType.DAY) + } else if (tab == mWeekTab && mCurrentView != ViewType.WEEK) { + mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, ViewType.WEEK) + } else if (tab == mMonthTab && mCurrentView != ViewType.MONTH) { + mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, ViewType.MONTH) } else { Log.w( - TAG, "TabSelected event from unknown tab: " - + if (tab == null) "null" else tab.getText() + TAG, "TabSelected event from unknown tab: " + + if (tab == null) "null" else tab.getText() ) Log.w( - TAG, "CurrentView:" + mCurrentView + " Tab:" + tab.toString() + " Day:" + mDayTab - + " Week:" + mWeekTab + " Month:" + mMonthTab + TAG, "CurrentView:" + mCurrentView + " Tab:" + tab.toString() + " Day:" + mDayTab + + " Week:" + mWeekTab + " Month:" + mMonthTab ) } } @Override - fun onTabReselected(tab: Tab?, ft: FragmentTransaction?) { + override fun onTabReselected(tab: Tab?, ft: FragmentTransaction?) { } @Override - fun onTabUnselected(tab: Tab?, ft: FragmentTransaction?) { + override fun onTabUnselected(tab: Tab?, ft: FragmentTransaction?) { } @Override - fun onNavigationItemSelected(itemPosition: Int, itemId: Long): Boolean { + override fun onNavigationItemSelected(itemPosition: Int, itemId: Long): Boolean { when (itemPosition) { CalendarViewAdapter.DAY_BUTTON_INDEX -> if (mCurrentView != ViewType.DAY) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY) + mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, + ViewType.DAY) } CalendarViewAdapter.WEEK_BUTTON_INDEX -> if (mCurrentView != ViewType.WEEK) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK) + mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, + ViewType.WEEK) } CalendarViewAdapter.MONTH_BUTTON_INDEX -> if (mCurrentView != ViewType.MONTH) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH) + mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, + ViewType.MONTH) } CalendarViewAdapter.AGENDA_BUTTON_INDEX -> { } @@ -964,7 +1035,7 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe Log.w(TAG, "ItemSelected event from unknown button: $itemPosition") Log.w( TAG, "CurrentView:" + mCurrentView + " Button:" + itemPosition + - " Day:" + mDayTab + " Week:" + mWeekTab + " Month:" + mMonthTab + " Day:" + mDayTab + " Week:" + mWeekTab + " Month:" + mMonthTab ) } } diff --git a/src/com/android/calendar/CalendarController.kt b/src/com/android/calendar/CalendarController.kt index 4d41d3b7..16ee8fdd 100644 --- a/src/com/android/calendar/CalendarController.kt +++ b/src/com/android/calendar/CalendarController.kt @@ -39,13 +39,13 @@ class CalendarController private constructor(context: Context?) { // This uses a LinkedHashMap so that we can replace fragments based on the // view id they are being expanded into since we can't guarantee a reference // to the handler will be findable - private val eventHandlers: LinkedHashMap = - LinkedHashMap(5) - private val mToBeRemovedEventHandlers: LinkedList = LinkedList() - private val mToBeAddedEventHandlers: LinkedHashMap = - LinkedHashMap() - private var mFirstEventHandler: Pair? = null - private var mToBeAddedFirstEventHandler: Pair? = null + private val eventHandlers: LinkedHashMap = + LinkedHashMap(5) + private val mToBeRemovedEventHandlers: LinkedList = LinkedList() + private val mToBeAddedEventHandlers: LinkedHashMap = + LinkedHashMap() + private var mFirstEventHandler: Pair? = null + private var mToBeAddedFirstEventHandler: Pair? = null @Volatile private var mDispatchInProgressCounter = 0 @@ -217,14 +217,14 @@ class CalendarController private constructor(context: Context?) { } fun sendEventRelatedEvent( - sender: Object?, - eventType: Long, - eventId: Long, - startMillis: Long, - endMillis: Long, - x: Int, - y: Int, - selectedMillis: Long + sender: Object?, + eventType: Long, + eventId: Long, + startMillis: Long, + endMillis: Long, + x: Int, + y: Int, + selectedMillis: Long ) { // TODO: pass the real allDay status or at least a status that says we don't know the // status and have the receiver query the data. @@ -252,15 +252,15 @@ class CalendarController private constructor(context: Context?) { * @param selectedMillis The time to specify as selected */ fun sendEventRelatedEventWithExtra( - sender: Object?, - eventType: Long, - eventId: Long, - startMillis: Long, - endMillis: Long, - x: Int, - y: Int, - extraLong: Long, - selectedMillis: Long + sender: Object?, + eventType: Long, + eventId: Long, + startMillis: Long, + endMillis: Long, + x: Int, + y: Int, + extraLong: Long, + selectedMillis: Long ) { sendEventRelatedEventWithExtraWithTitleWithCalendarId( sender, eventType, eventId, @@ -285,17 +285,17 @@ class CalendarController private constructor(context: Context?) { * @param calendarId The id of the calendar which the event belongs to */ fun sendEventRelatedEventWithExtraWithTitleWithCalendarId( - sender: Object?, - eventType: Long, - eventId: Long, - startMillis: Long, - endMillis: Long, - x: Int, - y: Int, - extraLong: Long, - selectedMillis: Long, - title: String?, - calendarId: Long + sender: Object?, + eventType: Long, + eventId: Long, + startMillis: Long, + endMillis: Long, + x: Int, + y: Int, + extraLong: Long, + selectedMillis: Long, + title: String?, + calendarId: Long ) { val info = EventInfo() info.eventType = eventType @@ -332,12 +332,12 @@ class CalendarController private constructor(context: Context?) { * @param viewType [ViewType] */ fun sendEvent( - sender: Object?, - eventType: Long, - start: Time?, - end: Time?, - eventId: Long, - viewType: Int + sender: Object?, + eventType: Long, + start: Time?, + end: Time?, + eventId: Long, + viewType: Int ) { sendEvent( sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null, @@ -349,15 +349,15 @@ class CalendarController private constructor(context: Context?) { * sendEvent() variant with extraLong, search query, and search component name. */ fun sendEvent( - sender: Object?, - eventType: Long, - start: Time?, - end: Time?, - eventId: Long, - viewType: Int, - extraLong: Long, - query: String?, - componentName: ComponentName? + sender: Object?, + eventType: Long, + start: Time?, + end: Time?, + eventId: Long, + viewType: Int, + extraLong: Long, + query: String?, + componentName: ComponentName? ) { sendEvent( sender, eventType, start, end, start, eventId, viewType, extraLong, query, @@ -366,16 +366,16 @@ class CalendarController private constructor(context: Context?) { } fun sendEvent( - sender: Object?, - eventType: Long, - start: Time?, - end: Time?, - selected: Time?, - eventId: Long, - viewType: Int, - extraLong: Long, - query: String?, - componentName: ComponentName? + sender: Object?, + eventType: Long, + start: Time?, + end: Time?, + selected: Time?, + eventId: Long, + viewType: Int, + extraLong: Long, + query: String?, + componentName: ComponentName? ) { val info = EventInfo() info.eventType = eventType @@ -509,11 +509,11 @@ class CalendarController private constructor(context: Context?) { handled = true } } - val handlers: MutableIterator> = eventHandlers.entries.iterator() + val handlers: MutableIterator> = eventHandlers.entries.iterator() while (handlers.hasNext()) { - val entry: MutableMap.MutableEntry = handlers.next() + val entry: MutableMap.MutableEntry = handlers.next() val key: Int = entry.key.toInt() val temp4 = mFirstEventHandler if (temp4 != null && key.toInt() == temp4.first.toInt()) { @@ -522,8 +522,8 @@ class CalendarController private constructor(context: Context?) { } val eventHandler: EventHandler = entry.value if (eventHandler != null && - eventHandler.supportedEventTypes and event.eventType != 0L) { - if (mToBeRemovedEventHandlers.contains(key as Integer)) { + eventHandler.supportedEventTypes and event.eventType != 0L) { + if (mToBeRemovedEventHandlers.contains(key)) { continue } eventHandler.handleEvent(event) @@ -565,7 +565,7 @@ class CalendarController private constructor(context: Context?) { * @param key The view id or placeholder for this handler * @param eventHandler Typically a fragment or activity in the calendar app */ - fun registerEventHandler(key: Integer, eventHandler: EventHandler?) { + fun registerEventHandler(key: Int, eventHandler: EventHandler?) { synchronized(this) { if (mDispatchInProgressCounter > 0) { mToBeAddedEventHandlers.put(key, @@ -576,18 +576,18 @@ class CalendarController private constructor(context: Context?) { } } - fun registerFirstEventHandler(key: Integer, eventHandler: EventHandler?) { + fun registerFirstEventHandler(key: Int, eventHandler: EventHandler?) { synchronized(this) { registerEventHandler(key, eventHandler) if (mDispatchInProgressCounter > 0) { - mToBeAddedFirstEventHandler = Pair(key, eventHandler) + mToBeAddedFirstEventHandler = Pair(key, eventHandler) } else { - mFirstEventHandler = Pair(key, eventHandler) + mFirstEventHandler = Pair(key, eventHandler) } } } - fun deregisterEventHandler(key: Integer) { + fun deregisterEventHandler(key: Int) { synchronized(this) { if (mDispatchInProgressCounter > 0) { // To avoid ConcurrencyException, stash away the event handler for now. -- cgit v1.2.3 From 7d1147c49d154fd1b75720c5be4cfb1756ca3cf2 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Wed, 30 Jun 2021 17:37:45 +0000 Subject: AOSP/Calendar - Copy of CalendarAppWidgetService.java The Java code in CalendarAppWidgetSerivce.java has been copied into a corresponding .kt file. Test: manual - opening both files shows that they are identical. Change-Id: I7f46bd71e83c1cc16faa99193511dd8b5f3abc35 --- .../calendar/widget/CalendarAppWidgetService.kt | 626 +++++++++++++++++++++ 1 file changed, 626 insertions(+) create mode 100644 src/com/android/calendar/widget/CalendarAppWidgetService.kt diff --git a/src/com/android/calendar/widget/CalendarAppWidgetService.kt b/src/com/android/calendar/widget/CalendarAppWidgetService.kt new file mode 100644 index 00000000..ec702c7c --- /dev/null +++ b/src/com/android/calendar/widget/CalendarAppWidgetService.kt @@ -0,0 +1,626 @@ +/* + * 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.widget; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.CursorLoader; +import android.content.Intent; +import android.content.Loader; +import android.content.res.Resources; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; +import android.os.Handler; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Instances; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Log; +import android.view.View; +import android.widget.RemoteViews; +import android.widget.RemoteViewsService; + +import com.android.calendar.R; +import com.android.calendar.Utils; +import com.android.calendar.widget.CalendarAppWidgetModel.DayInfo; +import com.android.calendar.widget.CalendarAppWidgetModel.EventInfo; +import com.android.calendar.widget.CalendarAppWidgetModel.RowInfo; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + + +public class CalendarAppWidgetService extends RemoteViewsService { + private static final String TAG = "CalendarWidget"; + + static final int EVENT_MIN_COUNT = 20; + static final int EVENT_MAX_COUNT = 100; + // Minimum delay between queries on the database for widget updates in ms + static final int WIDGET_UPDATE_THROTTLE = 500; + + private static final String EVENT_SORT_ORDER = Instances.START_DAY + " ASC, " + + Instances.START_MINUTE + " ASC, " + Instances.END_DAY + " ASC, " + + Instances.END_MINUTE + " ASC LIMIT " + EVENT_MAX_COUNT; + + private static final String EVENT_SELECTION = Calendars.VISIBLE + "=1"; + private static final String EVENT_SELECTION_HIDE_DECLINED = Calendars.VISIBLE + "=1 AND " + + Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED; + + static final String[] EVENT_PROJECTION = new String[] { + Instances.ALL_DAY, + Instances.BEGIN, + Instances.END, + Instances.TITLE, + Instances.EVENT_LOCATION, + Instances.EVENT_ID, + Instances.START_DAY, + Instances.END_DAY, + Instances.DISPLAY_COLOR, // If SDK < 16, set to Instances.CALENDAR_COLOR. + Instances.SELF_ATTENDEE_STATUS, + }; + + static final int INDEX_ALL_DAY = 0; + static final int INDEX_BEGIN = 1; + static final int INDEX_END = 2; + static final int INDEX_TITLE = 3; + static final int INDEX_EVENT_LOCATION = 4; + static final int INDEX_EVENT_ID = 5; + static final int INDEX_START_DAY = 6; + static final int INDEX_END_DAY = 7; + static final int INDEX_COLOR = 8; + static final int INDEX_SELF_ATTENDEE_STATUS = 9; + + static { + if (!Utils.isJellybeanOrLater()) { + EVENT_PROJECTION[INDEX_COLOR] = Instances.CALENDAR_COLOR; + } + } + static final int MAX_DAYS = 7; + + private static final long SEARCH_DURATION = MAX_DAYS * DateUtils.DAY_IN_MILLIS; + + /** + * Update interval used when no next-update calculated, or bad trigger time in past. + * Unit: milliseconds. + */ + private static final long UPDATE_TIME_NO_EVENTS = DateUtils.HOUR_IN_MILLIS * 6; + + @Override + public RemoteViewsFactory onGetViewFactory(Intent intent) { + return new CalendarFactory(getApplicationContext(), intent); + } + + public static class CalendarFactory extends BroadcastReceiver implements + RemoteViewsService.RemoteViewsFactory, Loader.OnLoadCompleteListener { + private static final boolean LOGD = false; + + // Suppress unnecessary logging about update time. Need to be static as this object is + // re-instanciated frequently. + // TODO: It seems loadData() is called via onCreate() four times, which should mean + // unnecessary CalendarFactory object is created and dropped. It is not efficient. + private static long sLastUpdateTime = UPDATE_TIME_NO_EVENTS; + + private Context mContext; + private Resources mResources; + private static CalendarAppWidgetModel mModel; + private static Object mLock = new Object(); + private static volatile int mSerialNum = 0; + private int mLastSerialNum = -1; + private CursorLoader mLoader; + private final Handler mHandler = new Handler(); + private static final AtomicInteger currentVersion = new AtomicInteger(0); + private final ExecutorService executor = Executors.newSingleThreadExecutor(); + private int mAppWidgetId; + private int mDeclinedColor; + private int mStandardColor; + private int mAllDayColor; + + private final Runnable mTimezoneChanged = new Runnable() { + @Override + public void run() { + if (mLoader != null) { + mLoader.forceLoad(); + } + } + }; + + private Runnable createUpdateLoaderRunnable(final String selection, + final PendingResult result, final int version) { + return new Runnable() { + @Override + public void run() { + // If there is a newer load request in the queue, skip loading. + if (mLoader != null && version >= currentVersion.get()) { + Uri uri = createLoaderUri(); + mLoader.setUri(uri); + mLoader.setSelection(selection); + synchronized (mLock) { + mLastSerialNum = ++mSerialNum; + } + mLoader.forceLoad(); + } + result.finish(); + } + }; + } + + protected CalendarFactory(Context context, Intent intent) { + mContext = context; + mResources = context.getResources(); + mAppWidgetId = intent.getIntExtra( + AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); + + mDeclinedColor = mResources.getColor(R.color.appwidget_item_declined_color); + mStandardColor = mResources.getColor(R.color.appwidget_item_standard_color); + mAllDayColor = mResources.getColor(R.color.appwidget_item_allday_color); + } + + public CalendarFactory() { + // This is being created as part of onReceive + + } + + @Override + public void onCreate() { + String selection = queryForSelection(); + initLoader(selection); + } + + @Override + public void onDataSetChanged() { + } + + @Override + public void onDestroy() { + if (mLoader != null) { + mLoader.reset(); + } + } + + @Override + public RemoteViews getLoadingView() { + RemoteViews views = new RemoteViews(mContext.getPackageName(), + R.layout.appwidget_loading); + return views; + } + + @Override + public RemoteViews getViewAt(int position) { + // we use getCount here so that it doesn't return null when empty + if (position < 0 || position >= getCount()) { + return null; + } + + if (mModel == null) { + RemoteViews views = new RemoteViews(mContext.getPackageName(), + R.layout.appwidget_loading); + final Intent intent = CalendarAppWidgetProvider.getLaunchFillInIntent(mContext, 0, + 0, 0, false); + views.setOnClickFillInIntent(R.id.appwidget_loading, intent); + return views; + + } + if (mModel.mEventInfos.isEmpty() || mModel.mRowInfos.isEmpty()) { + RemoteViews views = new RemoteViews(mContext.getPackageName(), + R.layout.appwidget_no_events); + final Intent intent = CalendarAppWidgetProvider.getLaunchFillInIntent(mContext, 0, + 0, 0, false); + views.setOnClickFillInIntent(R.id.appwidget_no_events, intent); + return views; + } + + RowInfo rowInfo = mModel.mRowInfos.get(position); + if (rowInfo.mType == RowInfo.TYPE_DAY) { + RemoteViews views = new RemoteViews(mContext.getPackageName(), + R.layout.appwidget_day); + DayInfo dayInfo = mModel.mDayInfos.get(rowInfo.mIndex); + updateTextView(views, R.id.date, View.VISIBLE, dayInfo.mDayLabel); + return views; + } else { + RemoteViews views; + final EventInfo eventInfo = mModel.mEventInfos.get(rowInfo.mIndex); + if (eventInfo.allDay) { + views = new RemoteViews(mContext.getPackageName(), + R.layout.widget_all_day_item); + } else { + views = new RemoteViews(mContext.getPackageName(), R.layout.widget_item); + } + int displayColor = Utils.getDisplayColorFromColor(eventInfo.color); + + final long now = System.currentTimeMillis(); + if (!eventInfo.allDay && eventInfo.start <= now && now <= eventInfo.end) { + views.setInt(R.id.widget_row, "setBackgroundResource", + R.drawable.agenda_item_bg_secondary); + } else { + views.setInt(R.id.widget_row, "setBackgroundResource", + R.drawable.agenda_item_bg_primary); + } + + if (!eventInfo.allDay) { + updateTextView(views, R.id.when, eventInfo.visibWhen, eventInfo.when); + updateTextView(views, R.id.where, eventInfo.visibWhere, eventInfo.where); + } + updateTextView(views, R.id.title, eventInfo.visibTitle, eventInfo.title); + + views.setViewVisibility(R.id.agenda_item_color, View.VISIBLE); + + int selfAttendeeStatus = eventInfo.selfAttendeeStatus; + if (eventInfo.allDay) { + if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_INVITED) { + views.setInt(R.id.agenda_item_color, "setImageResource", + R.drawable.widget_chip_not_responded_bg); + views.setInt(R.id.title, "setTextColor", displayColor); + } else { + views.setInt(R.id.agenda_item_color, "setImageResource", + R.drawable.widget_chip_responded_bg); + views.setInt(R.id.title, "setTextColor", mAllDayColor); + } + if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) { + // 40% opacity + views.setInt(R.id.agenda_item_color, "setColorFilter", + Utils.getDeclinedColorFromColor(displayColor)); + } else { + views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor); + } + } else if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) { + views.setInt(R.id.title, "setTextColor", mDeclinedColor); + views.setInt(R.id.when, "setTextColor", mDeclinedColor); + views.setInt(R.id.where, "setTextColor", mDeclinedColor); + views.setInt(R.id.agenda_item_color, "setImageResource", + R.drawable.widget_chip_responded_bg); + // 40% opacity + views.setInt(R.id.agenda_item_color, "setColorFilter", + Utils.getDeclinedColorFromColor(displayColor)); + } else { + views.setInt(R.id.title, "setTextColor", mStandardColor); + views.setInt(R.id.when, "setTextColor", mStandardColor); + views.setInt(R.id.where, "setTextColor", mStandardColor); + if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_INVITED) { + views.setInt(R.id.agenda_item_color, "setImageResource", + R.drawable.widget_chip_not_responded_bg); + } else { + views.setInt(R.id.agenda_item_color, "setImageResource", + R.drawable.widget_chip_responded_bg); + } + views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor); + } + + long start = eventInfo.start; + long end = eventInfo.end; + // An element in ListView. + if (eventInfo.allDay) { + String tz = Utils.getTimeZone(mContext, null); + Time recycle = new Time(); + start = Utils.convertAlldayLocalToUTC(recycle, start, tz); + end = Utils.convertAlldayLocalToUTC(recycle, end, tz); + } + final Intent fillInIntent = CalendarAppWidgetProvider.getLaunchFillInIntent( + mContext, eventInfo.id, start, end, eventInfo.allDay); + views.setOnClickFillInIntent(R.id.widget_row, fillInIntent); + return views; + } + } + + @Override + public int getViewTypeCount() { + return 5; + } + + @Override + public int getCount() { + // if there are no events, we still return 1 to represent the "no + // events" view + if (mModel == null) { + return 1; + } + return Math.max(1, mModel.mRowInfos.size()); + } + + @Override + public long getItemId(int position) { + if (mModel == null || mModel.mRowInfos.isEmpty() || position >= getCount()) { + return 0; + } + RowInfo rowInfo = mModel.mRowInfos.get(position); + if (rowInfo.mType == RowInfo.TYPE_DAY) { + return rowInfo.mIndex; + } + EventInfo eventInfo = mModel.mEventInfos.get(rowInfo.mIndex); + long prime = 31; + long result = 1; + result = prime * result + (int) (eventInfo.id ^ (eventInfo.id >>> 32)); + result = prime * result + (int) (eventInfo.start ^ (eventInfo.start >>> 32)); + return result; + } + + @Override + public boolean hasStableIds() { + return true; + } + + /** + * Query across all calendars for upcoming event instances from now + * until some time in the future. Widen the time range that we query by + * one day on each end so that we can catch all-day events. All-day + * events are stored starting at midnight in UTC but should be included + * in the list of events starting at midnight local time. This may fetch + * more events than we actually want, so we filter them out later. + * + * @param selection The selection string for the loader to filter the query with. + */ + public void initLoader(String selection) { + if (LOGD) + Log.d(TAG, "Querying for widget events..."); + + // Search for events from now until some time in the future + Uri uri = createLoaderUri(); + mLoader = new CursorLoader(mContext, uri, EVENT_PROJECTION, selection, null, + EVENT_SORT_ORDER); + mLoader.setUpdateThrottle(WIDGET_UPDATE_THROTTLE); + synchronized (mLock) { + mLastSerialNum = ++mSerialNum; + } + mLoader.registerListener(mAppWidgetId, this); + mLoader.startLoading(); + + } + + /** + * This gets the selection string for the loader. This ends up doing a query in the + * shared preferences. + */ + private String queryForSelection() { + return Utils.getHideDeclinedEvents(mContext) ? EVENT_SELECTION_HIDE_DECLINED + : EVENT_SELECTION; + } + + /** + * @return The uri for the loader + */ + private Uri createLoaderUri() { + long now = System.currentTimeMillis(); + // Add a day on either side to catch all-day events + long begin = now - DateUtils.DAY_IN_MILLIS; + long end = now + SEARCH_DURATION + DateUtils.DAY_IN_MILLIS; + + Uri uri = Uri.withAppendedPath(Instances.CONTENT_URI, Long.toString(begin) + "/" + end); + return uri; + } + + /* @VisibleForTesting */ + protected static CalendarAppWidgetModel buildAppWidgetModel( + Context context, Cursor cursor, String timeZone) { + CalendarAppWidgetModel model = new CalendarAppWidgetModel(context, timeZone); + model.buildFromCursor(cursor, timeZone); + return model; + } + + /** + * Calculates and returns the next time we should push widget updates. + */ + private long calculateUpdateTime(CalendarAppWidgetModel model, long now, String timeZone) { + // Make sure an update happens at midnight or earlier + long minUpdateTime = getNextMidnightTimeMillis(timeZone); + for (EventInfo event : model.mEventInfos) { + final long start; + final long end; + start = event.start; + end = event.end; + + // We want to update widget when we enter/exit time range of an event. + if (now < start) { + minUpdateTime = Math.min(minUpdateTime, start); + } else if (now < end) { + minUpdateTime = Math.min(minUpdateTime, end); + } + } + return minUpdateTime; + } + + private static long getNextMidnightTimeMillis(String timezone) { + Time time = new Time(); + time.setToNow(); + time.monthDay++; + time.hour = 0; + time.minute = 0; + time.second = 0; + long midnightDeviceTz = time.normalize(true); + + time.timezone = timezone; + time.setToNow(); + time.monthDay++; + time.hour = 0; + time.minute = 0; + time.second = 0; + long midnightHomeTz = time.normalize(true); + + return Math.min(midnightDeviceTz, midnightHomeTz); + } + + static void updateTextView(RemoteViews views, int id, int visibility, String string) { + views.setViewVisibility(id, visibility); + if (visibility == View.VISIBLE) { + views.setTextViewText(id, string); + } + } + + /* + * (non-Javadoc) + * @see + * android.content.Loader.OnLoadCompleteListener#onLoadComplete(android + * .content.Loader, java.lang.Object) + */ + @Override + public void onLoadComplete(Loader loader, Cursor cursor) { + if (cursor == null) { + return; + } + // If a newer update has happened since we started clean up and + // return + synchronized (mLock) { + if (cursor.isClosed()) { + Log.wtf(TAG, "Got a closed cursor from onLoadComplete"); + return; + } + + if (mLastSerialNum != mSerialNum) { + return; + } + + final long now = System.currentTimeMillis(); + String tz = Utils.getTimeZone(mContext, mTimezoneChanged); + + // Copy it to a local static cursor. + MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); + try { + mModel = buildAppWidgetModel(mContext, matrixCursor, tz); + } finally { + if (matrixCursor != null) { + matrixCursor.close(); + } + + if (cursor != null) { + cursor.close(); + } + } + + // Schedule an alarm to wake ourselves up for the next update. + // We also cancel + // all existing wake-ups because PendingIntents don't match + // against extras. + long triggerTime = calculateUpdateTime(mModel, now, tz); + + // If no next-update calculated, or bad trigger time in past, + // schedule + // update about six hours from now. + if (triggerTime < now) { + Log.w(TAG, "Encountered bad trigger time " + formatDebugTime(triggerTime, now)); + triggerTime = now + UPDATE_TIME_NO_EVENTS; + } + + final AlarmManager alertManager = (AlarmManager) mContext + .getSystemService(Context.ALARM_SERVICE); + final PendingIntent pendingUpdate = CalendarAppWidgetProvider + .getUpdateIntent(mContext); + + alertManager.cancel(pendingUpdate); + alertManager.set(AlarmManager.RTC, triggerTime, pendingUpdate); + Time time = new Time(Utils.getTimeZone(mContext, null)); + time.setToNow(); + + if (time.normalize(true) != sLastUpdateTime) { + Time time2 = new Time(Utils.getTimeZone(mContext, null)); + time2.set(sLastUpdateTime); + time2.normalize(true); + if (time.year != time2.year || time.yearDay != time2.yearDay) { + final Intent updateIntent = new Intent( + Utils.getWidgetUpdateAction(mContext)); + mContext.sendBroadcast(updateIntent); + } + + sLastUpdateTime = time.toMillis(true); + } + + AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext); + if (widgetManager == null) { + return; + } + if (mAppWidgetId == -1) { + int[] ids = widgetManager.getAppWidgetIds(CalendarAppWidgetProvider + .getComponentName(mContext)); + + widgetManager.notifyAppWidgetViewDataChanged(ids, R.id.events_list); + } else { + widgetManager.notifyAppWidgetViewDataChanged(mAppWidgetId, R.id.events_list); + } + } + } + + @Override + public void onReceive(Context context, Intent intent) { + if (LOGD) + Log.d(TAG, "AppWidgetService received an intent. It was " + intent.toString()); + mContext = context; + + // We cannot do any queries from the UI thread, so push the 'selection' query + // to a background thread. However the implementation of the latter query + // (cursor loading) uses CursorLoader which must be initiated from the UI thread, + // so there is some convoluted handshaking here. + // + // Note that as currently implemented, this must run in a single threaded executor + // or else the loads may be run out of order. + // + // TODO: Remove use of mHandler and CursorLoader, and do all the work synchronously + // in the background thread. All the handshaking going on here between the UI and + // background thread with using goAsync, mHandler, and CursorLoader is confusing. + final PendingResult result = goAsync(); + executor.submit(new Runnable() { + @Override + public void run() { + // We always complete queryForSelection() even if the load task ends up being + // canceled because of a more recent one. Optimizing this to allow + // canceling would require keeping track of all the PendingResults + // (from goAsync) to abort them. Defer this until it becomes a problem. + final String selection = queryForSelection(); + + if (mLoader == null) { + mAppWidgetId = -1; + mHandler.post(new Runnable() { + @Override + public void run() { + initLoader(selection); + result.finish(); + } + }); + } else { + mHandler.post(createUpdateLoaderRunnable(selection, result, + currentVersion.incrementAndGet())); + } + } + }); + } + } + + /** + * Format given time for debugging output. + * + * @param unixTime Target time to report. + * @param now Current system time from {@link System#currentTimeMillis()} + * for calculating time difference. + */ + static String formatDebugTime(long unixTime, long now) { + Time time = new Time(); + time.set(unixTime); + + long delta = unixTime - now; + if (delta > DateUtils.MINUTE_IN_MILLIS) { + delta /= DateUtils.MINUTE_IN_MILLIS; + return String.format("[%d] %s (%+d mins)", unixTime, + time.format("%H:%M:%S"), delta); + } else { + delta /= DateUtils.SECOND_IN_MILLIS; + return String.format("[%d] %s (%+d secs)", unixTime, + time.format("%H:%M:%S"), delta); + } + } +} -- cgit v1.2.3 From 55abf946c60789d249cbb98dbf1d0da5d59bc00c Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Wed, 30 Jun 2021 17:40:56 +0000 Subject: AOSP/Calendar - Initial conversion of CalendarAppWidgetService This is how Android Studio's built in converter converted CalendarAppWidgetService.java to Kotlin without any additional edits. Change-Id: I9746433948abd781d74ee957f7485d24a1f9321b --- .../calendar/widget/CalendarAppWidgetService.kt | 827 ++++++++++----------- 1 file changed, 410 insertions(+), 417 deletions(-) diff --git a/src/com/android/calendar/widget/CalendarAppWidgetService.kt b/src/com/android/calendar/widget/CalendarAppWidgetService.kt index ec702c7c..1a72eee9 100644 --- a/src/com/android/calendar/widget/CalendarAppWidgetService.kt +++ b/src/com/android/calendar/widget/CalendarAppWidgetService.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,349 +13,351 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar.widget + +import android.app.AlarmManager + +class CalendarAppWidgetService : RemoteViewsService() { + companion object { + private const val TAG = "CalendarWidget" + const val EVENT_MIN_COUNT = 20 + const val EVENT_MAX_COUNT = 100 + + // Minimum delay between queries on the database for widget updates in ms + const val WIDGET_UPDATE_THROTTLE = 500 + private val EVENT_SORT_ORDER: String = (Instances.START_DAY.toString() + " ASC, " + + Instances.START_MINUTE + " ASC, " + Instances.END_DAY + " ASC, " + + Instances.END_MINUTE + " ASC LIMIT " + EVENT_MAX_COUNT) + private val EVENT_SELECTION: String = Calendars.VISIBLE.toString() + "=1" + private val EVENT_SELECTION_HIDE_DECLINED: String = + (Calendars.VISIBLE.toString() + "=1 AND " + + Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED) + val EVENT_PROJECTION = arrayOf( + Instances.ALL_DAY, + Instances.BEGIN, + Instances.END, + Instances.TITLE, + Instances.EVENT_LOCATION, + Instances.EVENT_ID, + Instances.START_DAY, + Instances.END_DAY, + Instances.DISPLAY_COLOR, // If SDK < 16, set to Instances.CALENDAR_COLOR. + Instances.SELF_ATTENDEE_STATUS + ) + const val INDEX_ALL_DAY = 0 + const val INDEX_BEGIN = 1 + const val INDEX_END = 2 + const val INDEX_TITLE = 3 + const val INDEX_EVENT_LOCATION = 4 + const val INDEX_EVENT_ID = 5 + const val INDEX_START_DAY = 6 + const val INDEX_END_DAY = 7 + const val INDEX_COLOR = 8 + const val INDEX_SELF_ATTENDEE_STATUS = 9 + const val MAX_DAYS = 7 + private val SEARCH_DURATION: Long = MAX_DAYS * DateUtils.DAY_IN_MILLIS -package com.android.calendar.widget; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.CursorLoader; -import android.content.Intent; -import android.content.Loader; -import android.content.res.Resources; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.net.Uri; -import android.os.Handler; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Instances; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.View; -import android.widget.RemoteViews; -import android.widget.RemoteViewsService; - -import com.android.calendar.R; -import com.android.calendar.Utils; -import com.android.calendar.widget.CalendarAppWidgetModel.DayInfo; -import com.android.calendar.widget.CalendarAppWidgetModel.EventInfo; -import com.android.calendar.widget.CalendarAppWidgetModel.RowInfo; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicInteger; - - -public class CalendarAppWidgetService extends RemoteViewsService { - private static final String TAG = "CalendarWidget"; - - static final int EVENT_MIN_COUNT = 20; - static final int EVENT_MAX_COUNT = 100; - // Minimum delay between queries on the database for widget updates in ms - static final int WIDGET_UPDATE_THROTTLE = 500; - - private static final String EVENT_SORT_ORDER = Instances.START_DAY + " ASC, " - + Instances.START_MINUTE + " ASC, " + Instances.END_DAY + " ASC, " - + Instances.END_MINUTE + " ASC LIMIT " + EVENT_MAX_COUNT; - - private static final String EVENT_SELECTION = Calendars.VISIBLE + "=1"; - private static final String EVENT_SELECTION_HIDE_DECLINED = Calendars.VISIBLE + "=1 AND " - + Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED; - - static final String[] EVENT_PROJECTION = new String[] { - Instances.ALL_DAY, - Instances.BEGIN, - Instances.END, - Instances.TITLE, - Instances.EVENT_LOCATION, - Instances.EVENT_ID, - Instances.START_DAY, - Instances.END_DAY, - Instances.DISPLAY_COLOR, // If SDK < 16, set to Instances.CALENDAR_COLOR. - Instances.SELF_ATTENDEE_STATUS, - }; - - static final int INDEX_ALL_DAY = 0; - static final int INDEX_BEGIN = 1; - static final int INDEX_END = 2; - static final int INDEX_TITLE = 3; - static final int INDEX_EVENT_LOCATION = 4; - static final int INDEX_EVENT_ID = 5; - static final int INDEX_START_DAY = 6; - static final int INDEX_END_DAY = 7; - static final int INDEX_COLOR = 8; - static final int INDEX_SELF_ATTENDEE_STATUS = 9; - - static { - if (!Utils.isJellybeanOrLater()) { - EVENT_PROJECTION[INDEX_COLOR] = Instances.CALENDAR_COLOR; - } - } - static final int MAX_DAYS = 7; + /** + * Update interval used when no next-update calculated, or bad trigger time in past. + * Unit: milliseconds. + */ + private val UPDATE_TIME_NO_EVENTS: Long = DateUtils.HOUR_IN_MILLIS * 6 - private static final long SEARCH_DURATION = MAX_DAYS * DateUtils.DAY_IN_MILLIS; + /** + * Format given time for debugging output. + * + * @param unixTime Target time to report. + * @param now Current system time from [System.currentTimeMillis] + * for calculating time difference. + */ + fun formatDebugTime(unixTime: Long, now: Long): String { + val time = Time() + time.set(unixTime) + var delta = unixTime - now + return if (delta > DateUtils.MINUTE_IN_MILLIS) { + delta /= DateUtils.MINUTE_IN_MILLIS + String.format( + "[%d] %s (%+d mins)", unixTime, + time.format("%H:%M:%S"), delta + ) + } else { + delta /= DateUtils.SECOND_IN_MILLIS + String.format( + "[%d] %s (%+d secs)", unixTime, + time.format("%H:%M:%S"), delta + ) + } + } - /** - * Update interval used when no next-update calculated, or bad trigger time in past. - * Unit: milliseconds. - */ - private static final long UPDATE_TIME_NO_EVENTS = DateUtils.HOUR_IN_MILLIS * 6; + init { + if (!Utils.isJellybeanOrLater()) { + EVENT_PROJECTION[INDEX_COLOR] = Instances.CALENDAR_COLOR + } + } + } @Override - public RemoteViewsFactory onGetViewFactory(Intent intent) { - return new CalendarFactory(getApplicationContext(), intent); + fun onGetViewFactory(intent: Intent): RemoteViewsFactory { + return CalendarFactory(getApplicationContext(), intent) } - public static class CalendarFactory extends BroadcastReceiver implements - RemoteViewsService.RemoteViewsFactory, Loader.OnLoadCompleteListener { - private static final boolean LOGD = false; - - // Suppress unnecessary logging about update time. Need to be static as this object is - // re-instanciated frequently. - // TODO: It seems loadData() is called via onCreate() four times, which should mean - // unnecessary CalendarFactory object is created and dropped. It is not efficient. - private static long sLastUpdateTime = UPDATE_TIME_NO_EVENTS; - - private Context mContext; - private Resources mResources; - private static CalendarAppWidgetModel mModel; - private static Object mLock = new Object(); - private static volatile int mSerialNum = 0; - private int mLastSerialNum = -1; - private CursorLoader mLoader; - private final Handler mHandler = new Handler(); - private static final AtomicInteger currentVersion = new AtomicInteger(0); - private final ExecutorService executor = Executors.newSingleThreadExecutor(); - private int mAppWidgetId; - private int mDeclinedColor; - private int mStandardColor; - private int mAllDayColor; - - private final Runnable mTimezoneChanged = new Runnable() { + class CalendarFactory : BroadcastReceiver, RemoteViewsService.RemoteViewsFactory, + Loader.OnLoadCompleteListener { + private var mContext: Context? = null + private var mResources: Resources? = null + private var mLastSerialNum = -1 + private var mLoader: CursorLoader? = null + private val mHandler: Handler = Handler() + private val executor: ExecutorService = Executors.newSingleThreadExecutor() + private var mAppWidgetId = 0 + private var mDeclinedColor = 0 + private var mStandardColor = 0 + private var mAllDayColor = 0 + private val mTimezoneChanged: Runnable = object : Runnable() { @Override - public void run() { + fun run() { if (mLoader != null) { - mLoader.forceLoad(); + mLoader.forceLoad() } } - }; + } - private Runnable createUpdateLoaderRunnable(final String selection, - final PendingResult result, final int version) { - return new Runnable() { + private fun createUpdateLoaderRunnable( + selection: String, + result: PendingResult, version: Int + ): Runnable { + return object : Runnable() { @Override - public void run() { + fun run() { // If there is a newer load request in the queue, skip loading. if (mLoader != null && version >= currentVersion.get()) { - Uri uri = createLoaderUri(); - mLoader.setUri(uri); - mLoader.setSelection(selection); - synchronized (mLock) { - mLastSerialNum = ++mSerialNum; - } - mLoader.forceLoad(); + val uri: Uri = createLoaderUri() + mLoader.setUri(uri) + mLoader.setSelection(selection) + synchronized(mLock) { mLastSerialNum = ++mSerialNum } + mLoader.forceLoad() } - result.finish(); + result.finish() } - }; + } } - protected CalendarFactory(Context context, Intent intent) { - mContext = context; - mResources = context.getResources(); + constructor(context: Context, intent: Intent) { + mContext = context + mResources = context.getResources() mAppWidgetId = intent.getIntExtra( - AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); - - mDeclinedColor = mResources.getColor(R.color.appwidget_item_declined_color); - mStandardColor = mResources.getColor(R.color.appwidget_item_standard_color); - mAllDayColor = mResources.getColor(R.color.appwidget_item_allday_color); + AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID + ) + mDeclinedColor = mResources.getColor(R.color.appwidget_item_declined_color) + mStandardColor = mResources.getColor(R.color.appwidget_item_standard_color) + mAllDayColor = mResources.getColor(R.color.appwidget_item_allday_color) } - public CalendarFactory() { + constructor() { // This is being created as part of onReceive - } @Override - public void onCreate() { - String selection = queryForSelection(); - initLoader(selection); + fun onCreate() { + val selection = queryForSelection() + initLoader(selection) } @Override - public void onDataSetChanged() { + fun onDataSetChanged() { } @Override - public void onDestroy() { + fun onDestroy() { if (mLoader != null) { - mLoader.reset(); + mLoader.reset() } } - @Override - public RemoteViews getLoadingView() { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_loading); - return views; - } + @get:Override val loadingView: RemoteViews + get() = RemoteViews( + mContext.getPackageName(), + R.layout.appwidget_loading + ) @Override - public RemoteViews getViewAt(int position) { + fun getViewAt(position: Int): RemoteViews? { // we use getCount here so that it doesn't return null when empty - if (position < 0 || position >= getCount()) { - return null; + if (position < 0 || position >= count) { + return null } - if (mModel == null) { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_loading); - final Intent intent = CalendarAppWidgetProvider.getLaunchFillInIntent(mContext, 0, - 0, 0, false); - views.setOnClickFillInIntent(R.id.appwidget_loading, intent); - return views; - + val views = RemoteViews( + mContext.getPackageName(), + R.layout.appwidget_loading + ) + val intent: Intent = CalendarAppWidgetProvider.getLaunchFillInIntent( + mContext, + 0, + 0, + 0, + false + ) + views.setOnClickFillInIntent(R.id.appwidget_loading, intent) + return views } if (mModel.mEventInfos.isEmpty() || mModel.mRowInfos.isEmpty()) { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_no_events); - final Intent intent = CalendarAppWidgetProvider.getLaunchFillInIntent(mContext, 0, - 0, 0, false); - views.setOnClickFillInIntent(R.id.appwidget_no_events, intent); - return views; + val views = RemoteViews( + mContext.getPackageName(), + R.layout.appwidget_no_events + ) + val intent: Intent = CalendarAppWidgetProvider.getLaunchFillInIntent( + mContext, + 0, + 0, + 0, + false + ) + views.setOnClickFillInIntent(R.id.appwidget_no_events, intent) + return views } - - RowInfo rowInfo = mModel.mRowInfos.get(position); - if (rowInfo.mType == RowInfo.TYPE_DAY) { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_day); - DayInfo dayInfo = mModel.mDayInfos.get(rowInfo.mIndex); - updateTextView(views, R.id.date, View.VISIBLE, dayInfo.mDayLabel); - return views; + val rowInfo: RowInfo = mModel.mRowInfos.get(position) + return if (rowInfo.mType === RowInfo.TYPE_DAY) { + val views = RemoteViews( + mContext.getPackageName(), + R.layout.appwidget_day + ) + val dayInfo: DayInfo = mModel.mDayInfos.get(rowInfo.mIndex) + updateTextView(views, R.id.date, View.VISIBLE, dayInfo.mDayLabel) + views } else { - RemoteViews views; - final EventInfo eventInfo = mModel.mEventInfos.get(rowInfo.mIndex); + val views: RemoteViews + val eventInfo: EventInfo = mModel.mEventInfos.get(rowInfo.mIndex) if (eventInfo.allDay) { - views = new RemoteViews(mContext.getPackageName(), - R.layout.widget_all_day_item); + views = RemoteViews( + mContext.getPackageName(), + R.layout.widget_all_day_item + ) } else { - views = new RemoteViews(mContext.getPackageName(), R.layout.widget_item); + views = RemoteViews(mContext.getPackageName(), R.layout.widget_item) } - int displayColor = Utils.getDisplayColorFromColor(eventInfo.color); - - final long now = System.currentTimeMillis(); + val displayColor: Int = Utils.getDisplayColorFromColor(eventInfo.color) + val now: Long = System.currentTimeMillis() if (!eventInfo.allDay && eventInfo.start <= now && now <= eventInfo.end) { - views.setInt(R.id.widget_row, "setBackgroundResource", - R.drawable.agenda_item_bg_secondary); + views.setInt( + R.id.widget_row, "setBackgroundResource", + R.drawable.agenda_item_bg_secondary + ) } else { - views.setInt(R.id.widget_row, "setBackgroundResource", - R.drawable.agenda_item_bg_primary); + views.setInt( + R.id.widget_row, "setBackgroundResource", + R.drawable.agenda_item_bg_primary + ) } - if (!eventInfo.allDay) { - updateTextView(views, R.id.when, eventInfo.visibWhen, eventInfo.when); - updateTextView(views, R.id.where, eventInfo.visibWhere, eventInfo.where); + updateTextView(views, R.id.`when`, eventInfo.visibWhen, eventInfo.`when`) + updateTextView(views, R.id.where, eventInfo.visibWhere, eventInfo.where) } - updateTextView(views, R.id.title, eventInfo.visibTitle, eventInfo.title); - - views.setViewVisibility(R.id.agenda_item_color, View.VISIBLE); - - int selfAttendeeStatus = eventInfo.selfAttendeeStatus; + updateTextView(views, R.id.title, eventInfo.visibTitle, eventInfo.title) + views.setViewVisibility(R.id.agenda_item_color, View.VISIBLE) + val selfAttendeeStatus: Int = eventInfo.selfAttendeeStatus if (eventInfo.allDay) { if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_INVITED) { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_not_responded_bg); - views.setInt(R.id.title, "setTextColor", displayColor); + views.setInt( + R.id.agenda_item_color, "setImageResource", + R.drawable.widget_chip_not_responded_bg + ) + views.setInt(R.id.title, "setTextColor", displayColor) } else { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_responded_bg); - views.setInt(R.id.title, "setTextColor", mAllDayColor); + views.setInt( + R.id.agenda_item_color, "setImageResource", + R.drawable.widget_chip_responded_bg + ) + views.setInt(R.id.title, "setTextColor", mAllDayColor) } if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) { // 40% opacity - views.setInt(R.id.agenda_item_color, "setColorFilter", - Utils.getDeclinedColorFromColor(displayColor)); + views.setInt( + R.id.agenda_item_color, "setColorFilter", + Utils.getDeclinedColorFromColor(displayColor) + ) } else { - views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor); + views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor) } } else if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) { - views.setInt(R.id.title, "setTextColor", mDeclinedColor); - views.setInt(R.id.when, "setTextColor", mDeclinedColor); - views.setInt(R.id.where, "setTextColor", mDeclinedColor); - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_responded_bg); + views.setInt(R.id.title, "setTextColor", mDeclinedColor) + views.setInt(R.id.`when`, "setTextColor", mDeclinedColor) + views.setInt(R.id.where, "setTextColor", mDeclinedColor) + views.setInt( + R.id.agenda_item_color, "setImageResource", + R.drawable.widget_chip_responded_bg + ) // 40% opacity - views.setInt(R.id.agenda_item_color, "setColorFilter", - Utils.getDeclinedColorFromColor(displayColor)); + views.setInt( + R.id.agenda_item_color, "setColorFilter", + Utils.getDeclinedColorFromColor(displayColor) + ) } else { - views.setInt(R.id.title, "setTextColor", mStandardColor); - views.setInt(R.id.when, "setTextColor", mStandardColor); - views.setInt(R.id.where, "setTextColor", mStandardColor); + views.setInt(R.id.title, "setTextColor", mStandardColor) + views.setInt(R.id.`when`, "setTextColor", mStandardColor) + views.setInt(R.id.where, "setTextColor", mStandardColor) if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_INVITED) { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_not_responded_bg); + views.setInt( + R.id.agenda_item_color, "setImageResource", + R.drawable.widget_chip_not_responded_bg + ) } else { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_responded_bg); + views.setInt( + R.id.agenda_item_color, "setImageResource", + R.drawable.widget_chip_responded_bg + ) } - views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor); + views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor) } - - long start = eventInfo.start; - long end = eventInfo.end; + var start: Long = eventInfo.start + var end: Long = eventInfo.end // An element in ListView. if (eventInfo.allDay) { - String tz = Utils.getTimeZone(mContext, null); - Time recycle = new Time(); - start = Utils.convertAlldayLocalToUTC(recycle, start, tz); - end = Utils.convertAlldayLocalToUTC(recycle, end, tz); + val tz: String = Utils.getTimeZone(mContext, null) + val recycle = Time() + start = Utils.convertAlldayLocalToUTC(recycle, start, tz) + end = Utils.convertAlldayLocalToUTC(recycle, end, tz) } - final Intent fillInIntent = CalendarAppWidgetProvider.getLaunchFillInIntent( - mContext, eventInfo.id, start, end, eventInfo.allDay); - views.setOnClickFillInIntent(R.id.widget_row, fillInIntent); - return views; + val fillInIntent: Intent = CalendarAppWidgetProvider.getLaunchFillInIntent( + mContext, eventInfo.id, start, end, eventInfo.allDay + ) + views.setOnClickFillInIntent(R.id.widget_row, fillInIntent) + views } } - @Override - public int getViewTypeCount() { - return 5; - } + @get:Override val viewTypeCount: Int + get() = 5 - @Override - public int getCount() { - // if there are no events, we still return 1 to represent the "no - // events" view - if (mModel == null) { - return 1; - } - return Math.max(1, mModel.mRowInfos.size()); - } + // if there are no events, we still return 1 to represent the "no + // events" view + @get:Override val count: Int + get() =// if there are no events, we still return 1 to represent the "no + // events" view + if (mModel == null) { + 1 + } else Math.max(1, mModel.mRowInfos.size()) @Override - public long getItemId(int position) { - if (mModel == null || mModel.mRowInfos.isEmpty() || position >= getCount()) { - return 0; + fun getItemId(position: Int): Long { + if (mModel == null || mModel.mRowInfos.isEmpty() || position >= count) { + return 0 } - RowInfo rowInfo = mModel.mRowInfos.get(position); - if (rowInfo.mType == RowInfo.TYPE_DAY) { - return rowInfo.mIndex; + val rowInfo: RowInfo = mModel.mRowInfos.get(position) + if (rowInfo.mType === RowInfo.TYPE_DAY) { + return rowInfo.mIndex } - EventInfo eventInfo = mModel.mEventInfos.get(rowInfo.mIndex); - long prime = 31; - long result = 1; - result = prime * result + (int) (eventInfo.id ^ (eventInfo.id >>> 32)); - result = prime * result + (int) (eventInfo.start ^ (eventInfo.start >>> 32)); - return result; + val eventInfo: EventInfo = mModel.mEventInfos.get(rowInfo.mIndex) + val prime: Long = 31 + var result: Long = 1 + result = prime * result + (eventInfo.id xor (eventInfo.id ushr 32)) as Int + result = prime * result + (eventInfo.start xor (eventInfo.start ushr 32)) as Int + return result } @Override - public boolean hasStableIds() { - return true; + fun hasStableIds(): Boolean { + return true } /** @@ -368,100 +370,68 @@ public class CalendarAppWidgetService extends RemoteViewsService { * * @param selection The selection string for the loader to filter the query with. */ - public void initLoader(String selection) { - if (LOGD) - Log.d(TAG, "Querying for widget events..."); + fun initLoader(selection: String?) { + if (LOGD) Log.d(TAG, "Querying for widget events...") // Search for events from now until some time in the future - Uri uri = createLoaderUri(); - mLoader = new CursorLoader(mContext, uri, EVENT_PROJECTION, selection, null, - EVENT_SORT_ORDER); - mLoader.setUpdateThrottle(WIDGET_UPDATE_THROTTLE); - synchronized (mLock) { - mLastSerialNum = ++mSerialNum; - } - mLoader.registerListener(mAppWidgetId, this); - mLoader.startLoading(); - + val uri: Uri = createLoaderUri() + mLoader = CursorLoader( + mContext, uri, EVENT_PROJECTION, selection, null, + EVENT_SORT_ORDER + ) + mLoader.setUpdateThrottle(WIDGET_UPDATE_THROTTLE) + synchronized(mLock) { mLastSerialNum = ++mSerialNum } + mLoader.registerListener(mAppWidgetId, this) + mLoader.startLoading() } /** * This gets the selection string for the loader. This ends up doing a query in the * shared preferences. */ - private String queryForSelection() { - return Utils.getHideDeclinedEvents(mContext) ? EVENT_SELECTION_HIDE_DECLINED - : EVENT_SELECTION; + private fun queryForSelection(): String { + return if (Utils.getHideDeclinedEvents(mContext)) EVENT_SELECTION_HIDE_DECLINED else EVENT_SELECTION } /** * @return The uri for the loader */ - private Uri createLoaderUri() { - long now = System.currentTimeMillis(); + private fun createLoaderUri(): Uri { + val now: Long = System.currentTimeMillis() // Add a day on either side to catch all-day events - long begin = now - DateUtils.DAY_IN_MILLIS; - long end = now + SEARCH_DURATION + DateUtils.DAY_IN_MILLIS; - - Uri uri = Uri.withAppendedPath(Instances.CONTENT_URI, Long.toString(begin) + "/" + end); - return uri; - } - - /* @VisibleForTesting */ - protected static CalendarAppWidgetModel buildAppWidgetModel( - Context context, Cursor cursor, String timeZone) { - CalendarAppWidgetModel model = new CalendarAppWidgetModel(context, timeZone); - model.buildFromCursor(cursor, timeZone); - return model; + val begin: Long = now - DateUtils.DAY_IN_MILLIS + val end: Long = + now + SEARCH_DURATION + DateUtils.DAY_IN_MILLIS + return Uri.withAppendedPath( + Instances.CONTENT_URI, + toString(begin) + "/" + end + ) } /** * Calculates and returns the next time we should push widget updates. */ - private long calculateUpdateTime(CalendarAppWidgetModel model, long now, String timeZone) { + private fun calculateUpdateTime( + model: CalendarAppWidgetModel?, + now: Long, + timeZone: String + ): Long { // Make sure an update happens at midnight or earlier - long minUpdateTime = getNextMidnightTimeMillis(timeZone); - for (EventInfo event : model.mEventInfos) { - final long start; - final long end; - start = event.start; - end = event.end; + var minUpdateTime = getNextMidnightTimeMillis(timeZone) + for (event in model.mEventInfos) { + val start: Long + val end: Long + start = event.start + end = event.end // We want to update widget when we enter/exit time range of an event. if (now < start) { - minUpdateTime = Math.min(minUpdateTime, start); + minUpdateTime = Math.min(minUpdateTime, start) } else if (now < end) { - minUpdateTime = Math.min(minUpdateTime, end); + minUpdateTime = Math.min(minUpdateTime, end) } } - return minUpdateTime; - } - - private static long getNextMidnightTimeMillis(String timezone) { - Time time = new Time(); - time.setToNow(); - time.monthDay++; - time.hour = 0; - time.minute = 0; - time.second = 0; - long midnightDeviceTz = time.normalize(true); - - time.timezone = timezone; - time.setToNow(); - time.monthDay++; - time.hour = 0; - time.minute = 0; - time.second = 0; - long midnightHomeTz = time.normalize(true); - - return Math.min(midnightDeviceTz, midnightHomeTz); - } - - static void updateTextView(RemoteViews views, int id, int visibility, String string) { - views.setViewVisibility(id, visibility); - if (visibility == View.VISIBLE) { - views.setTextViewText(id, string); - } + return minUpdateTime } /* @@ -471,36 +441,33 @@ public class CalendarAppWidgetService extends RemoteViewsService { * .content.Loader, java.lang.Object) */ @Override - public void onLoadComplete(Loader loader, Cursor cursor) { + fun onLoadComplete(loader: Loader?, cursor: Cursor?) { if (cursor == null) { - return; + return } // If a newer update has happened since we started clean up and // return - synchronized (mLock) { + synchronized(mLock) { if (cursor.isClosed()) { - Log.wtf(TAG, "Got a closed cursor from onLoadComplete"); - return; + Log.wtf(TAG, "Got a closed cursor from onLoadComplete") + return } - if (mLastSerialNum != mSerialNum) { - return; + return } - - final long now = System.currentTimeMillis(); - String tz = Utils.getTimeZone(mContext, mTimezoneChanged); + val now: Long = System.currentTimeMillis() + val tz: String = Utils.getTimeZone(mContext, mTimezoneChanged) // Copy it to a local static cursor. - MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); + val matrixCursor: MatrixCursor = Utils.matrixCursorFromCursor(cursor) try { - mModel = buildAppWidgetModel(mContext, matrixCursor, tz); + mModel = buildAppWidgetModel(mContext, matrixCursor, tz) } finally { if (matrixCursor != null) { - matrixCursor.close(); + matrixCursor.close() } - if (cursor != null) { - cursor.close(); + cursor.close() } } @@ -508,59 +475,55 @@ public class CalendarAppWidgetService extends RemoteViewsService { // We also cancel // all existing wake-ups because PendingIntents don't match // against extras. - long triggerTime = calculateUpdateTime(mModel, now, tz); + var triggerTime = calculateUpdateTime(mModel, now, tz) // If no next-update calculated, or bad trigger time in past, // schedule // update about six hours from now. if (triggerTime < now) { - Log.w(TAG, "Encountered bad trigger time " + formatDebugTime(triggerTime, now)); - triggerTime = now + UPDATE_TIME_NO_EVENTS; + Log.w(TAG, "Encountered bad trigger time " + formatDebugTime(triggerTime, now)) + triggerTime = now + UPDATE_TIME_NO_EVENTS } - - final AlarmManager alertManager = (AlarmManager) mContext - .getSystemService(Context.ALARM_SERVICE); - final PendingIntent pendingUpdate = CalendarAppWidgetProvider - .getUpdateIntent(mContext); - - alertManager.cancel(pendingUpdate); - alertManager.set(AlarmManager.RTC, triggerTime, pendingUpdate); - Time time = new Time(Utils.getTimeZone(mContext, null)); - time.setToNow(); - - if (time.normalize(true) != sLastUpdateTime) { - Time time2 = new Time(Utils.getTimeZone(mContext, null)); - time2.set(sLastUpdateTime); - time2.normalize(true); - if (time.year != time2.year || time.yearDay != time2.yearDay) { - final Intent updateIntent = new Intent( - Utils.getWidgetUpdateAction(mContext)); - mContext.sendBroadcast(updateIntent); + val alertManager: AlarmManager = mContext + .getSystemService(Context.ALARM_SERVICE) as AlarmManager + val pendingUpdate: PendingIntent = CalendarAppWidgetProvider + .getUpdateIntent(mContext) + alertManager.cancel(pendingUpdate) + alertManager.set(AlarmManager.RTC, triggerTime, pendingUpdate) + val time = Time(Utils.getTimeZone(mContext, null)) + time.setToNow() + if (time.normalize(true) !== sLastUpdateTime) { + val time2 = Time(Utils.getTimeZone(mContext, null)) + time2.set(sLastUpdateTime) + time2.normalize(true) + if (time.year !== time2.year || time.yearDay !== time2.yearDay) { + val updateIntent = Intent( + Utils.getWidgetUpdateAction(mContext) + ) + mContext.sendBroadcast(updateIntent) } - - sLastUpdateTime = time.toMillis(true); + sLastUpdateTime = time.toMillis(true) } - - AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext); + val widgetManager: AppWidgetManager = AppWidgetManager.getInstance(mContext) if (widgetManager == null) { - return; + return } if (mAppWidgetId == -1) { - int[] ids = widgetManager.getAppWidgetIds(CalendarAppWidgetProvider - .getComponentName(mContext)); - - widgetManager.notifyAppWidgetViewDataChanged(ids, R.id.events_list); + val ids: IntArray = widgetManager.getAppWidgetIds( + CalendarAppWidgetProvider + .getComponentName(mContext) + ) + widgetManager.notifyAppWidgetViewDataChanged(ids, R.id.events_list) } else { - widgetManager.notifyAppWidgetViewDataChanged(mAppWidgetId, R.id.events_list); + widgetManager.notifyAppWidgetViewDataChanged(mAppWidgetId, R.id.events_list) } } } @Override - public void onReceive(Context context, Intent intent) { - if (LOGD) - Log.d(TAG, "AppWidgetService received an intent. It was " + intent.toString()); - mContext = context; + fun onReceive(context: Context?, intent: Intent) { + if (LOGD) Log.d(TAG, "AppWidgetService received an intent. It was " + intent.toString()) + mContext = context // We cannot do any queries from the UI thread, so push the 'selection' query // to a background thread. However the implementation of the latter query @@ -573,54 +536,84 @@ public class CalendarAppWidgetService extends RemoteViewsService { // TODO: Remove use of mHandler and CursorLoader, and do all the work synchronously // in the background thread. All the handshaking going on here between the UI and // background thread with using goAsync, mHandler, and CursorLoader is confusing. - final PendingResult result = goAsync(); - executor.submit(new Runnable() { + val result: PendingResult = goAsync() + executor.submit(object : Runnable() { @Override - public void run() { + fun run() { // We always complete queryForSelection() even if the load task ends up being // canceled because of a more recent one. Optimizing this to allow // canceling would require keeping track of all the PendingResults // (from goAsync) to abort them. Defer this until it becomes a problem. - final String selection = queryForSelection(); - + val selection = queryForSelection() if (mLoader == null) { - mAppWidgetId = -1; - mHandler.post(new Runnable() { + mAppWidgetId = -1 + mHandler.post(object : Runnable() { @Override - public void run() { - initLoader(selection); - result.finish(); + fun run() { + initLoader(selection) + result.finish() } - }); + }) } else { - mHandler.post(createUpdateLoaderRunnable(selection, result, - currentVersion.incrementAndGet())); + mHandler.post( + createUpdateLoaderRunnable( + selection, result, + currentVersion.incrementAndGet() + ) + ) } } - }); + }) } - } - /** - * Format given time for debugging output. - * - * @param unixTime Target time to report. - * @param now Current system time from {@link System#currentTimeMillis()} - * for calculating time difference. - */ - static String formatDebugTime(long unixTime, long now) { - Time time = new Time(); - time.set(unixTime); - - long delta = unixTime - now; - if (delta > DateUtils.MINUTE_IN_MILLIS) { - delta /= DateUtils.MINUTE_IN_MILLIS; - return String.format("[%d] %s (%+d mins)", unixTime, - time.format("%H:%M:%S"), delta); - } else { - delta /= DateUtils.SECOND_IN_MILLIS; - return String.format("[%d] %s (%+d secs)", unixTime, - time.format("%H:%M:%S"), delta); + companion object { + private const val LOGD = false + + // Suppress unnecessary logging about update time. Need to be static as this object is + // re-instantiated frequently. + // TODO: It seems loadData() is called via onCreate() four times, which should mean + // unnecessary CalendarFactory object is created and dropped. It is not efficient. + private var sLastUpdateTime = UPDATE_TIME_NO_EVENTS + private var mModel: CalendarAppWidgetModel? = null + private val mLock: Object = Object() + + @Volatile + private var mSerialNum = 0 + private val currentVersion: AtomicInteger = AtomicInteger(0) + + /* @VisibleForTesting */ + protected fun buildAppWidgetModel( + context: Context?, cursor: Cursor?, timeZone: String? + ): CalendarAppWidgetModel { + val model = CalendarAppWidgetModel(context, timeZone) + model.buildFromCursor(cursor, timeZone) + return model + } + + private fun getNextMidnightTimeMillis(timezone: String): Long { + val time = Time() + time.setToNow() + time.monthDay++ + time.hour = 0 + time.minute = 0 + time.second = 0 + val midnightDeviceTz: Long = time.normalize(true) + time.timezone = timezone + time.setToNow() + time.monthDay++ + time.hour = 0 + time.minute = 0 + time.second = 0 + val midnightHomeTz: Long = time.normalize(true) + return Math.min(midnightDeviceTz, midnightHomeTz) + } + + fun updateTextView(views: RemoteViews, id: Int, visibility: Int, string: String?) { + views.setViewVisibility(id, visibility) + if (visibility == View.VISIBLE) { + views.setTextViewText(id, string) + } + } } } -} +} \ No newline at end of file -- cgit v1.2.3 From 0c62d323b773fa1a540d7fab41cb1b6d850a16f3 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Wed, 30 Jun 2021 21:23:25 +0000 Subject: AOSP/Calendar - CalendarAppWidgetService fully converted with bp file This is the fully converted and touched-up Kotlin file after the automatic converter was run. The converter failed to assign certain variables to be nullable and even did not add the "override" modifier. One interesting issue was the "buildAppWidgetModel()" static method whose static visibility was failing compilation and unit tests. The fix to this was to add the "internal" modifier to the companion object that stored the JVM static methods in Kotlin. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.202 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 7m 44s Total aggregated tests run time: 7m 44s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.81 tests/sec [376 tests / 464727 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 6929 ms || clean = 2179 ms Total preparation time: 6s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 9m 14s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: Ic23ffc6c4db458574d417751f531d17cec8723c4 --- Android.bp | 2 + .../calendar/widget/CalendarAppWidgetService.kt | 288 ++++++++++++--------- 2 files changed, 169 insertions(+), 121 deletions(-) diff --git a/Android.bp b/Android.bp index 142971be..af827ed4 100644 --- a/Android.bp +++ b/Android.bp @@ -30,6 +30,8 @@ exclude_srcsd = [ "src/**/calendar/month/MonthByWeekFragment.java", "src/**/calendar/widget/CalendarAppWidgetModel.java", "src/**/calendar/widget/CalendarAppWidgetProvider.java", + "src/**/calendar/widget/CalendarAppWidgetService.java", + "src/**/calendar/AllInOneActivity.java", "src/**/calendar/CalendarController.java", "src/**/calendar/DayOfMonthDrawable.java", "src/**/calendar/Event.java", diff --git a/src/com/android/calendar/widget/CalendarAppWidgetService.kt b/src/com/android/calendar/widget/CalendarAppWidgetService.kt index 1a72eee9..114fdf12 100644 --- a/src/com/android/calendar/widget/CalendarAppWidgetService.kt +++ b/src/com/android/calendar/widget/CalendarAppWidgetService.kt @@ -16,6 +16,35 @@ package com.android.calendar.widget import android.app.AlarmManager +import android.app.PendingIntent +import android.appwidget.AppWidgetManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.CursorLoader +import android.content.Intent +import android.content.Loader +import android.content.res.Resources +import android.database.Cursor +import android.database.MatrixCursor +import android.net.Uri +import android.os.Handler +import android.provider.CalendarContract.Attendees +import android.provider.CalendarContract.Calendars +import android.provider.CalendarContract.Instances +import android.text.format.DateUtils +import android.text.format.Time +import android.util.Log +import android.view.View +import android.widget.RemoteViews +import android.widget.RemoteViewsService +import com.android.calendar.R +import com.android.calendar.Utils +import com.android.calendar.widget.CalendarAppWidgetModel.DayInfo +import com.android.calendar.widget.CalendarAppWidgetModel.EventInfo +import com.android.calendar.widget.CalendarAppWidgetModel.RowInfo +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.atomic.AtomicInteger class CalendarAppWidgetService : RemoteViewsService() { companion object { @@ -25,13 +54,14 @@ class CalendarAppWidgetService : RemoteViewsService() { // Minimum delay between queries on the database for widget updates in ms const val WIDGET_UPDATE_THROTTLE = 500 - private val EVENT_SORT_ORDER: String = (Instances.START_DAY.toString() + " ASC, " - + Instances.START_MINUTE + " ASC, " + Instances.END_DAY + " ASC, " - + Instances.END_MINUTE + " ASC LIMIT " + EVENT_MAX_COUNT) + private val EVENT_SORT_ORDER: String = (Instances.START_DAY.toString() + " ASC, " + + Instances.START_MINUTE + " ASC, " + Instances.END_DAY + " ASC, " + + Instances.END_MINUTE + " ASC LIMIT " + EVENT_MAX_COUNT) private val EVENT_SELECTION: String = Calendars.VISIBLE.toString() + "=1" private val EVENT_SELECTION_HIDE_DECLINED: String = - (Calendars.VISIBLE.toString() + "=1 AND " - + Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED) + (Calendars.VISIBLE.toString() + "=1 AND " + + Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED) + @JvmField val EVENT_PROJECTION = arrayOf( Instances.ALL_DAY, Instances.BEGIN, @@ -41,7 +71,7 @@ class CalendarAppWidgetService : RemoteViewsService() { Instances.EVENT_ID, Instances.START_DAY, Instances.END_DAY, - Instances.DISPLAY_COLOR, // If SDK < 16, set to Instances.CALENDAR_COLOR. + Instances.DISPLAY_COLOR, // If SDK < 16, set to Instances.CALENDAR_COLOR. Instances.SELF_ATTENDEE_STATUS ) const val INDEX_ALL_DAY = 0 @@ -97,7 +127,7 @@ class CalendarAppWidgetService : RemoteViewsService() { } @Override - fun onGetViewFactory(intent: Intent): RemoteViewsFactory { + override fun onGetViewFactory(intent: Intent): RemoteViewsFactory { return CalendarFactory(getApplicationContext(), intent) } @@ -113,29 +143,30 @@ class CalendarAppWidgetService : RemoteViewsService() { private var mDeclinedColor = 0 private var mStandardColor = 0 private var mAllDayColor = 0 - private val mTimezoneChanged: Runnable = object : Runnable() { + private val mTimezoneChanged: Runnable = object : Runnable { @Override - fun run() { + override fun run() { if (mLoader != null) { - mLoader.forceLoad() + mLoader?.forceLoad() } } } private fun createUpdateLoaderRunnable( selection: String, - result: PendingResult, version: Int + result: PendingResult, + version: Int ): Runnable { - return object : Runnable() { + return object : Runnable { @Override - fun run() { + override fun run() { // If there is a newer load request in the queue, skip loading. if (mLoader != null && version >= currentVersion.get()) { val uri: Uri = createLoaderUri() - mLoader.setUri(uri) - mLoader.setSelection(selection) + mLoader?.setUri(uri) + mLoader?.setSelection(selection) synchronized(mLock) { mLastSerialNum = ++mSerialNum } - mLoader.forceLoad() + mLoader?.forceLoad() } result.finish() } @@ -148,9 +179,9 @@ class CalendarAppWidgetService : RemoteViewsService() { mAppWidgetId = intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID ) - mDeclinedColor = mResources.getColor(R.color.appwidget_item_declined_color) - mStandardColor = mResources.getColor(R.color.appwidget_item_standard_color) - mAllDayColor = mResources.getColor(R.color.appwidget_item_allday_color) + mDeclinedColor = mResources?.getColor(R.color.appwidget_item_declined_color) as Int + mStandardColor = mResources?.getColor(R.color.appwidget_item_standard_color) as Int + mAllDayColor = mResources?.getColor(R.color.appwidget_item_allday_color) as Int } constructor() { @@ -158,37 +189,37 @@ class CalendarAppWidgetService : RemoteViewsService() { } @Override - fun onCreate() { + override fun onCreate() { val selection = queryForSelection() initLoader(selection) } @Override - fun onDataSetChanged() { + override fun onDataSetChanged() { } @Override - fun onDestroy() { + override fun onDestroy() { if (mLoader != null) { - mLoader.reset() + mLoader?.reset() } } - @get:Override val loadingView: RemoteViews - get() = RemoteViews( - mContext.getPackageName(), - R.layout.appwidget_loading - ) + @Override + override fun getLoadingView(): RemoteViews { + val views = RemoteViews(mContext?.getPackageName(), R.layout.appwidget_loading) + return views + } @Override - fun getViewAt(position: Int): RemoteViews? { + override fun getViewAt(position: Int): RemoteViews? { // we use getCount here so that it doesn't return null when empty - if (position < 0 || position >= count) { + if (position < 0 || position >= getCount()) { return null } if (mModel == null) { val views = RemoteViews( - mContext.getPackageName(), + mContext?.getPackageName(), R.layout.appwidget_loading ) val intent: Intent = CalendarAppWidgetProvider.getLaunchFillInIntent( @@ -201,9 +232,9 @@ class CalendarAppWidgetService : RemoteViewsService() { views.setOnClickFillInIntent(R.id.appwidget_loading, intent) return views } - if (mModel.mEventInfos.isEmpty() || mModel.mRowInfos.isEmpty()) { + if (mModel!!.mEventInfos!!.isEmpty() || mModel!!.mRowInfos!!.isEmpty()) { val views = RemoteViews( - mContext.getPackageName(), + mContext?.getPackageName(), R.layout.appwidget_no_events ) val intent: Intent = CalendarAppWidgetProvider.getLaunchFillInIntent( @@ -216,138 +247,144 @@ class CalendarAppWidgetService : RemoteViewsService() { views.setOnClickFillInIntent(R.id.appwidget_no_events, intent) return views } - val rowInfo: RowInfo = mModel.mRowInfos.get(position) - return if (rowInfo.mType === RowInfo.TYPE_DAY) { + val rowInfo: RowInfo? = mModel?.mRowInfos?.get(position) + return if (rowInfo!!.mType == RowInfo!!.TYPE_DAY) { val views = RemoteViews( - mContext.getPackageName(), + mContext?.getPackageName(), R.layout.appwidget_day ) - val dayInfo: DayInfo = mModel.mDayInfos.get(rowInfo.mIndex) - updateTextView(views, R.id.date, View.VISIBLE, dayInfo.mDayLabel) + val dayInfo: DayInfo? = mModel?.mDayInfos?.get(rowInfo!!.mIndex) + updateTextView(views, R.id.date, View.VISIBLE, dayInfo!!.mDayLabel) views } else { - val views: RemoteViews - val eventInfo: EventInfo = mModel.mEventInfos.get(rowInfo.mIndex) - if (eventInfo.allDay) { + val views: RemoteViews? + val eventInfo: EventInfo? = mModel?.mEventInfos?.get(rowInfo.mIndex) + if (eventInfo!!.allDay) { views = RemoteViews( - mContext.getPackageName(), + mContext?.getPackageName(), R.layout.widget_all_day_item ) } else { - views = RemoteViews(mContext.getPackageName(), R.layout.widget_item) + views = RemoteViews(mContext?.getPackageName(), R.layout.widget_item) } - val displayColor: Int = Utils.getDisplayColorFromColor(eventInfo.color) + val displayColor: Int = Utils.getDisplayColorFromColor(eventInfo!!.color) val now: Long = System.currentTimeMillis() - if (!eventInfo.allDay && eventInfo.start <= now && now <= eventInfo.end) { - views.setInt( + if (!eventInfo!!.allDay && eventInfo!!.start <= now && now <= eventInfo!!.end) { + views?.setInt( R.id.widget_row, "setBackgroundResource", R.drawable.agenda_item_bg_secondary ) } else { - views.setInt( + views?.setInt( R.id.widget_row, "setBackgroundResource", R.drawable.agenda_item_bg_primary ) } - if (!eventInfo.allDay) { - updateTextView(views, R.id.`when`, eventInfo.visibWhen, eventInfo.`when`) - updateTextView(views, R.id.where, eventInfo.visibWhere, eventInfo.where) + if (!eventInfo?.allDay) { + updateTextView(views, R.id.`when`, eventInfo?.visibWhen + as Int, eventInfo?.`when`) + updateTextView(views, R.id.where, eventInfo?.visibWhere + as Int, eventInfo?.where) } - updateTextView(views, R.id.title, eventInfo.visibTitle, eventInfo.title) + updateTextView(views, R.id.title, eventInfo?.visibTitle as Int, eventInfo?.title) views.setViewVisibility(R.id.agenda_item_color, View.VISIBLE) - val selfAttendeeStatus: Int = eventInfo.selfAttendeeStatus - if (eventInfo.allDay) { + val selfAttendeeStatus: Int = eventInfo?.selfAttendeeStatus as Int + if (eventInfo!!.allDay) { if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_INVITED) { - views.setInt( + views?.setInt( R.id.agenda_item_color, "setImageResource", R.drawable.widget_chip_not_responded_bg ) - views.setInt(R.id.title, "setTextColor", displayColor) + views?.setInt(R.id.title, "setTextColor", displayColor) } else { - views.setInt( + views?.setInt( R.id.agenda_item_color, "setImageResource", R.drawable.widget_chip_responded_bg ) - views.setInt(R.id.title, "setTextColor", mAllDayColor) + views?.setInt(R.id.title, "setTextColor", mAllDayColor) } if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) { // 40% opacity - views.setInt( + views?.setInt( R.id.agenda_item_color, "setColorFilter", Utils.getDeclinedColorFromColor(displayColor) ) } else { - views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor) + views?.setInt(R.id.agenda_item_color, "setColorFilter", displayColor) } } else if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) { - views.setInt(R.id.title, "setTextColor", mDeclinedColor) - views.setInt(R.id.`when`, "setTextColor", mDeclinedColor) - views.setInt(R.id.where, "setTextColor", mDeclinedColor) - views.setInt( + views?.setInt(R.id.title, "setTextColor", mDeclinedColor) + views?.setInt(R.id.`when`, "setTextColor", mDeclinedColor) + views?.setInt(R.id.where, "setTextColor", mDeclinedColor) + views?.setInt( R.id.agenda_item_color, "setImageResource", R.drawable.widget_chip_responded_bg ) // 40% opacity - views.setInt( + views?.setInt( R.id.agenda_item_color, "setColorFilter", Utils.getDeclinedColorFromColor(displayColor) ) } else { - views.setInt(R.id.title, "setTextColor", mStandardColor) - views.setInt(R.id.`when`, "setTextColor", mStandardColor) - views.setInt(R.id.where, "setTextColor", mStandardColor) + views?.setInt(R.id.title, "setTextColor", mStandardColor) + views?.setInt(R.id.`when`, "setTextColor", mStandardColor) + views?.setInt(R.id.where, "setTextColor", mStandardColor) if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_INVITED) { - views.setInt( + views?.setInt( R.id.agenda_item_color, "setImageResource", R.drawable.widget_chip_not_responded_bg ) } else { - views.setInt( + views?.setInt( R.id.agenda_item_color, "setImageResource", R.drawable.widget_chip_responded_bg ) } - views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor) + views?.setInt(R.id.agenda_item_color, "setColorFilter", displayColor) } - var start: Long = eventInfo.start - var end: Long = eventInfo.end + var start: Long = eventInfo?.start as Long + var end: Long = eventInfo?.end as Long // An element in ListView. - if (eventInfo.allDay) { - val tz: String = Utils.getTimeZone(mContext, null) + if (eventInfo!!.allDay) { + val tz: String? = Utils.getTimeZone(mContext, null) val recycle = Time() - start = Utils.convertAlldayLocalToUTC(recycle, start, tz) - end = Utils.convertAlldayLocalToUTC(recycle, end, tz) + start = Utils.convertAlldayLocalToUTC(recycle, start, tz as String) + end = Utils.convertAlldayLocalToUTC(recycle, end, tz as String) } val fillInIntent: Intent = CalendarAppWidgetProvider.getLaunchFillInIntent( - mContext, eventInfo.id, start, end, eventInfo.allDay + mContext, eventInfo?.id, start, end, eventInfo?.allDay ) views.setOnClickFillInIntent(R.id.widget_row, fillInIntent) views } } - @get:Override val viewTypeCount: Int - get() = 5 + @Override + override fun getViewTypeCount(): Int { + return 5 + } - // if there are no events, we still return 1 to represent the "no - // events" view - @get:Override val count: Int - get() =// if there are no events, we still return 1 to represent the "no - // events" view - if (mModel == null) { - 1 - } else Math.max(1, mModel.mRowInfos.size()) + @Override + override fun getCount(): Int { + // if there are no events, we still return 1 to represent the "no + // events" view + if (mModel == null) { + return 1 + } + return Math.max(1, mModel?.mRowInfos?.size as Int) + } @Override - fun getItemId(position: Int): Long { - if (mModel == null || mModel.mRowInfos.isEmpty() || position >= count) { + override fun getItemId(position: Int): Long { + if (mModel == null || mModel?.mRowInfos?.isEmpty() as Boolean || + position >= getCount()) { return 0 } - val rowInfo: RowInfo = mModel.mRowInfos.get(position) - if (rowInfo.mType === RowInfo.TYPE_DAY) { - return rowInfo.mIndex + val rowInfo: RowInfo = mModel?.mRowInfos?.get(position) as RowInfo + if (rowInfo.mType == RowInfo.TYPE_DAY) { + return rowInfo.mIndex.toLong() } - val eventInfo: EventInfo = mModel.mEventInfos.get(rowInfo.mIndex) + val eventInfo: EventInfo = mModel?.mEventInfos?.get(rowInfo.mIndex) as EventInfo val prime: Long = 31 var result: Long = 1 result = prime * result + (eventInfo.id xor (eventInfo.id ushr 32)) as Int @@ -356,7 +393,7 @@ class CalendarAppWidgetService : RemoteViewsService() { } @Override - fun hasStableIds(): Boolean { + override fun hasStableIds(): Boolean { return true } @@ -379,10 +416,10 @@ class CalendarAppWidgetService : RemoteViewsService() { mContext, uri, EVENT_PROJECTION, selection, null, EVENT_SORT_ORDER ) - mLoader.setUpdateThrottle(WIDGET_UPDATE_THROTTLE) + mLoader?.setUpdateThrottle(WIDGET_UPDATE_THROTTLE.toLong()) synchronized(mLock) { mLastSerialNum = ++mSerialNum } - mLoader.registerListener(mAppWidgetId, this) - mLoader.startLoading() + mLoader?.registerListener(mAppWidgetId, this) + mLoader?.startLoading() } /** @@ -390,7 +427,8 @@ class CalendarAppWidgetService : RemoteViewsService() { * shared preferences. */ private fun queryForSelection(): String { - return if (Utils.getHideDeclinedEvents(mContext)) EVENT_SELECTION_HIDE_DECLINED else EVENT_SELECTION + return if (Utils.getHideDeclinedEvents(mContext)) EVENT_SELECTION_HIDE_DECLINED + else EVENT_SELECTION } /** @@ -404,7 +442,7 @@ class CalendarAppWidgetService : RemoteViewsService() { now + SEARCH_DURATION + DateUtils.DAY_IN_MILLIS return Uri.withAppendedPath( Instances.CONTENT_URI, - toString(begin) + "/" + end + begin.toString() + "/" + end ) } @@ -412,7 +450,7 @@ class CalendarAppWidgetService : RemoteViewsService() { * Calculates and returns the next time we should push widget updates. */ private fun calculateUpdateTime( - model: CalendarAppWidgetModel?, + model: CalendarAppWidgetModel, now: Long, timeZone: String ): Long { @@ -441,7 +479,7 @@ class CalendarAppWidgetService : RemoteViewsService() { * .content.Loader, java.lang.Object) */ @Override - fun onLoadComplete(loader: Loader?, cursor: Cursor?) { + override fun onLoadComplete(loader: Loader?, cursor: Cursor?) { if (cursor == null) { return } @@ -456,18 +494,18 @@ class CalendarAppWidgetService : RemoteViewsService() { return } val now: Long = System.currentTimeMillis() - val tz: String = Utils.getTimeZone(mContext, mTimezoneChanged) + val tz: String? = Utils.getTimeZone(mContext, mTimezoneChanged) // Copy it to a local static cursor. - val matrixCursor: MatrixCursor = Utils.matrixCursorFromCursor(cursor) + val matrixCursor: MatrixCursor? = Utils.matrixCursorFromCursor(cursor) try { mModel = buildAppWidgetModel(mContext, matrixCursor, tz) } finally { if (matrixCursor != null) { - matrixCursor.close() + matrixCursor?.close() } if (cursor != null) { - cursor.close() + cursor?.close() } } @@ -475,7 +513,8 @@ class CalendarAppWidgetService : RemoteViewsService() { // We also cancel // all existing wake-ups because PendingIntents don't match // against extras. - var triggerTime = calculateUpdateTime(mModel, now, tz) + var triggerTime = calculateUpdateTime(mModel as CalendarAppWidgetModel, + now, tz as String) // If no next-update calculated, or bad trigger time in past, // schedule @@ -485,7 +524,7 @@ class CalendarAppWidgetService : RemoteViewsService() { triggerTime = now + UPDATE_TIME_NO_EVENTS } val alertManager: AlarmManager = mContext - .getSystemService(Context.ALARM_SERVICE) as AlarmManager + ?.getSystemService(Context.ALARM_SERVICE) as AlarmManager val pendingUpdate: PendingIntent = CalendarAppWidgetProvider .getUpdateIntent(mContext) alertManager.cancel(pendingUpdate) @@ -498,9 +537,9 @@ class CalendarAppWidgetService : RemoteViewsService() { time2.normalize(true) if (time.year !== time2.year || time.yearDay !== time2.yearDay) { val updateIntent = Intent( - Utils.getWidgetUpdateAction(mContext) + Utils.getWidgetUpdateAction(mContext as Context) ) - mContext.sendBroadcast(updateIntent) + mContext?.sendBroadcast(updateIntent) } sLastUpdateTime = time.toMillis(true) } @@ -521,7 +560,7 @@ class CalendarAppWidgetService : RemoteViewsService() { } @Override - fun onReceive(context: Context?, intent: Intent) { + override fun onReceive(context: Context?, intent: Intent) { if (LOGD) Log.d(TAG, "AppWidgetService received an intent. It was " + intent.toString()) mContext = context @@ -537,9 +576,9 @@ class CalendarAppWidgetService : RemoteViewsService() { // in the background thread. All the handshaking going on here between the UI and // background thread with using goAsync, mHandler, and CursorLoader is confusing. val result: PendingResult = goAsync() - executor.submit(object : Runnable() { + executor.submit(object : Runnable { @Override - fun run() { + override fun run() { // We always complete queryForSelection() even if the load task ends up being // canceled because of a more recent one. Optimizing this to allow // canceling would require keeping track of all the PendingResults @@ -547,9 +586,9 @@ class CalendarAppWidgetService : RemoteViewsService() { val selection = queryForSelection() if (mLoader == null) { mAppWidgetId = -1 - mHandler.post(object : Runnable() { + mHandler.post(object : Runnable { @Override - fun run() { + override fun run() { initLoader(selection) result.finish() } @@ -566,7 +605,7 @@ class CalendarAppWidgetService : RemoteViewsService() { }) } - companion object { + internal companion object { private const val LOGD = false // Suppress unnecessary logging about update time. Need to be static as this object is @@ -582,15 +621,17 @@ class CalendarAppWidgetService : RemoteViewsService() { private val currentVersion: AtomicInteger = AtomicInteger(0) /* @VisibleForTesting */ - protected fun buildAppWidgetModel( - context: Context?, cursor: Cursor?, timeZone: String? + @JvmStatic protected fun buildAppWidgetModel( + context: Context?, + cursor: Cursor?, + timeZone: String? ): CalendarAppWidgetModel { - val model = CalendarAppWidgetModel(context, timeZone) - model.buildFromCursor(cursor, timeZone) + val model = CalendarAppWidgetModel(context as Context, timeZone) + model.buildFromCursor(cursor as Cursor, timeZone) return model } - private fun getNextMidnightTimeMillis(timezone: String): Long { + @JvmStatic private fun getNextMidnightTimeMillis(timezone: String): Long { val time = Time() time.setToNow() time.monthDay++ @@ -608,7 +649,12 @@ class CalendarAppWidgetService : RemoteViewsService() { return Math.min(midnightDeviceTz, midnightHomeTz) } - fun updateTextView(views: RemoteViews, id: Int, visibility: Int, string: String?) { + @JvmStatic fun updateTextView( + views: RemoteViews, + id: Int, + visibility: Int, + string: String? + ) { views.setViewVisibility(id, visibility) if (visibility == View.VISIBLE) { views.setTextViewText(id, string) -- cgit v1.2.3 From eeb807f78665ec690b050ae8d116bccbc6026e64 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 1 Jul 2021 15:27:20 +0000 Subject: AOSP/Calendar - Copy of DayView.java The Java code in DayView.java has been copied into a corresponding .kt file. Test: manual - opening both .java and .kt files shows that both are identical. Change-Id: Iff918c5b12a25a124fe8498a7a5e631500ec55a7 --- src/com/android/calendar/DayView.kt | 4008 +++++++++++++++++++++++++++++++++++ 1 file changed, 4008 insertions(+) create mode 100644 src/com/android/calendar/DayView.kt diff --git a/src/com/android/calendar/DayView.kt b/src/com/android/calendar/DayView.kt new file mode 100644 index 00000000..2fc00b3c --- /dev/null +++ b/src/com/android/calendar/DayView.kt @@ -0,0 +1,4008 @@ +/* + * 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.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.AlertDialog; +import android.app.Service; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.database.Cursor; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.Style; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Handler; +import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Events; +import android.text.Layout.Alignment; +import android.text.SpannableStringBuilder; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.text.style.StyleSpan; +import android.util.Log; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.Interpolator; +import android.view.animation.TranslateAnimation; +import android.widget.EdgeEffect; +import android.widget.ImageView; +import android.widget.OverScroller; +import android.widget.PopupWindow; +import android.widget.TextView; +import android.widget.ViewSwitcher; + +import com.android.calendar.CalendarController.EventType; +import com.android.calendar.CalendarController.ViewType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Formatter; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * View for multi-day view. So far only 1 and 7 day have been tested. + */ +public class DayView extends View implements View.OnCreateContextMenuListener, + ScaleGestureDetector.OnScaleGestureListener, View.OnClickListener, View.OnLongClickListener + { + private static String TAG = "DayView"; + private static boolean DEBUG = false; + private static boolean DEBUG_SCALING = false; + private static final String PERIOD_SPACE = ". "; + + private static float mScale = 0; // Used for supporting different screen densities + private static final long INVALID_EVENT_ID = -1; //This is used for remembering a null event + // Duration of the allday expansion + private static final long ANIMATION_DURATION = 400; + // duration of the more allday event text fade + private static final long ANIMATION_SECONDARY_DURATION = 200; + // duration of the scroll to go to a specified time + private static final int GOTO_SCROLL_DURATION = 200; + // duration for events' cross-fade animation + private static final int EVENTS_CROSS_FADE_DURATION = 400; + // duration to show the event clicked + private static final int CLICK_DISPLAY_DURATION = 50; + + private static final int MENU_DAY = 3; + private static final int MENU_EVENT_VIEW = 5; + private static final int MENU_EVENT_CREATE = 6; + private static final int MENU_EVENT_EDIT = 7; + private static final int MENU_EVENT_DELETE = 8; + + private static int DEFAULT_CELL_HEIGHT = 64; + private static int MAX_CELL_HEIGHT = 150; + private static int MIN_Y_SPAN = 100; + + private boolean mOnFlingCalled; + private boolean mStartingScroll = false; + protected boolean mPaused = true; + private Handler mHandler; + /** + * ID of the last event which was displayed with the toast popup. + * + * This is used to prevent popping up multiple quick views for the same event, especially + * during calendar syncs. This becomes valid when an event is selected, either by default + * on starting calendar or by scrolling to an event. It becomes invalid when the user + * explicitly scrolls to an empty time slot, changes views, or deletes the event. + */ + private long mLastPopupEventID; + + protected Context mContext; + + private static final String[] CALENDARS_PROJECTION = new String[] { + Calendars._ID, // 0 + Calendars.CALENDAR_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 int FROM_NONE = 0; + private static final int FROM_ABOVE = 1; + private static final int FROM_BELOW = 2; + private static final int FROM_LEFT = 4; + private static final int FROM_RIGHT = 8; + + private static final int ACCESS_LEVEL_NONE = 0; + private static final int ACCESS_LEVEL_DELETE = 1; + private static final int ACCESS_LEVEL_EDIT = 2; + + private static int mHorizontalSnapBackThreshold = 128; + + private final ContinueScroll mContinueScroll = new ContinueScroll(); + + // Make this visible within the package for more informative debugging + Time mBaseDate; + private Time mCurrentTime; + //Update the current time line every five minutes if the window is left open that long + private static final int UPDATE_CURRENT_TIME_DELAY = 300000; + private final UpdateCurrentTime mUpdateCurrentTime = new UpdateCurrentTime(); + private int mTodayJulianDay; + + private final Typeface mBold = Typeface.DEFAULT_BOLD; + private int mFirstJulianDay; + private int mLoadedFirstJulianDay = -1; + private int mLastJulianDay; + + private int mMonthLength; + private int mFirstVisibleDate; + private int mFirstVisibleDayOfWeek; + private int[] mEarliestStartHour; // indexed by the week day offset + private boolean[] mHasAllDayEvent; // indexed by the week day offset + private String mEventCountTemplate; + private Event mClickedEvent; // The event the user clicked on + private Event mSavedClickedEvent; + private static int mOnDownDelay; + private int mClickedYLocation; + private long mDownTouchTime; + + private int mEventsAlpha = 255; + private ObjectAnimator mEventsCrossFadeAnimation; + + protected static StringBuilder mStringBuilder = new StringBuilder(50); + // TODO recreate formatter when locale changes + protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); + + private final Runnable mTZUpdater = new Runnable() { + @Override + public void run() { + String tz = Utils.getTimeZone(mContext, this); + mBaseDate.timezone = tz; + mBaseDate.normalize(true); + mCurrentTime.switchTimezone(tz); + invalidate(); + } + }; + + // Sets the "clicked" color from the clicked event + private final Runnable mSetClick = new Runnable() { + @Override + public void run() { + mClickedEvent = mSavedClickedEvent; + mSavedClickedEvent = null; + DayView.this.invalidate(); + } + }; + + // Clears the "clicked" color from the clicked event and launch the event + private final Runnable mClearClick = new Runnable() { + @Override + public void run() { + if (mClickedEvent != null) { + mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, mClickedEvent.id, + mClickedEvent.startMillis, mClickedEvent.endMillis, + DayView.this.getWidth() / 2, mClickedYLocation, + getSelectedTimeInMillis()); + } + mClickedEvent = null; + DayView.this.invalidate(); + } + }; + + private final TodayAnimatorListener mTodayAnimatorListener = new TodayAnimatorListener(); + + class TodayAnimatorListener extends AnimatorListenerAdapter { + private volatile Animator mAnimator = null; + private volatile boolean mFadingIn = false; + + @Override + public void onAnimationEnd(Animator animation) { + synchronized (this) { + if (mAnimator != animation) { + animation.removeAllListeners(); + animation.cancel(); + return; + } + if (mFadingIn) { + if (mTodayAnimator != null) { + mTodayAnimator.removeAllListeners(); + mTodayAnimator.cancel(); + } + mTodayAnimator = ObjectAnimator + .ofInt(DayView.this, "animateTodayAlpha", 255, 0); + mAnimator = mTodayAnimator; + mFadingIn = false; + mTodayAnimator.addListener(this); + mTodayAnimator.setDuration(600); + mTodayAnimator.start(); + } else { + mAnimateToday = false; + mAnimateTodayAlpha = 0; + mAnimator.removeAllListeners(); + mAnimator = null; + mTodayAnimator = null; + invalidate(); + } + } + } + + public void setAnimator(Animator animation) { + mAnimator = animation; + } + + public void setFadingIn(boolean fadingIn) { + mFadingIn = fadingIn; + } + + } + + AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mScrolling = true; + } + + @Override + public void onAnimationCancel(Animator animation) { + mScrolling = false; + } + + @Override + public void onAnimationEnd(Animator animation) { + mScrolling = false; + resetSelectedHour(); + invalidate(); + } + }; + + /** + * This variable helps to avoid unnecessarily reloading events by keeping + * track of the start millis parameter used for the most recent loading + * of events. If the next reload matches this, then the events are not + * reloaded. To force a reload, set this to zero (this is set to zero + * in the method clearCachedEvents()). + */ + private long mLastReloadMillis; + + private ArrayList mEvents = new ArrayList(); + private ArrayList mAllDayEvents = new ArrayList(); + private StaticLayout[] mLayouts = null; + private StaticLayout[] mAllDayLayouts = null; + private int mSelectionDay; // Julian day + private int mSelectionHour; + + boolean mSelectionAllday; + + // Current selection info for accessibility + private int mSelectionDayForAccessibility; // Julian day + private int mSelectionHourForAccessibility; + private Event mSelectedEventForAccessibility; + // Last selection info for accessibility + private int mLastSelectionDayForAccessibility; + private int mLastSelectionHourForAccessibility; + private Event mLastSelectedEventForAccessibility; + + + /** Width of a day or non-conflicting event */ + private int mCellWidth; + + // Pre-allocate these objects and re-use them + private final Rect mRect = new Rect(); + private final Rect mDestRect = new Rect(); + private final Rect mSelectionRect = new Rect(); + // This encloses the more allDay events icon + private final Rect mExpandAllDayRect = new Rect(); + // TODO Clean up paint usage + private final Paint mPaint = new Paint(); + private final Paint mEventTextPaint = new Paint(); + private final Paint mSelectionPaint = new Paint(); + private float[] mLines; + + private int mFirstDayOfWeek; // First day of the week + + private PopupWindow mPopup; + private View mPopupView; + + // The number of milliseconds to show the popup window + private static final int POPUP_DISMISS_DELAY = 3000; + private final DismissPopup mDismissPopup = new DismissPopup(); + + private boolean mRemeasure = true; + + private final EventLoader mEventLoader; + protected final EventGeometry mEventGeometry; + + private static float GRID_LINE_LEFT_MARGIN = 0; + private static final float GRID_LINE_INNER_WIDTH = 1; + + private static final int DAY_GAP = 1; + private static final int HOUR_GAP = 1; + // This is the standard height of an allday event with no restrictions + private static int SINGLE_ALLDAY_HEIGHT = 34; + /** + * This is the minimum desired height of a allday event. + * When unexpanded, allday events will use this height. + * When expanded allDay events will attempt to grow to fit all + * events at this height. + */ + private static float MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = 28.0F; // in pixels + /** + * This is how big the unexpanded allday height is allowed to be. + * It will get adjusted based on screen size + */ + private static int MAX_UNEXPANDED_ALLDAY_HEIGHT = + (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4); + /** + * This is the minimum size reserved for displaying regular events. + * The expanded allDay region can't expand into this. + */ + private static int MIN_HOURS_HEIGHT = 180; + private static int ALLDAY_TOP_MARGIN = 1; + // The largest a single allDay event will become. + private static int MAX_HEIGHT_OF_ONE_ALLDAY_EVENT = 34; + + private static int HOURS_TOP_MARGIN = 2; + private static int HOURS_LEFT_MARGIN = 2; + private static int HOURS_RIGHT_MARGIN = 4; + private static int HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN; + private static int NEW_EVENT_MARGIN = 4; + private static int NEW_EVENT_WIDTH = 2; + private static int NEW_EVENT_MAX_LENGTH = 16; + + private static int CURRENT_TIME_LINE_SIDE_BUFFER = 4; + private static int CURRENT_TIME_LINE_TOP_OFFSET = 2; + + /* package */ static final int MINUTES_PER_HOUR = 60; + /* package */ static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * 24; + /* package */ static final int MILLIS_PER_MINUTE = 60 * 1000; + /* package */ static final int MILLIS_PER_HOUR = (3600 * 1000); + /* package */ static final int MILLIS_PER_DAY = MILLIS_PER_HOUR * 24; + + // More events text will transition between invisible and this alpha + private static final int MORE_EVENTS_MAX_ALPHA = 0x4C; + private static int DAY_HEADER_ONE_DAY_LEFT_MARGIN = 0; + private static int DAY_HEADER_ONE_DAY_RIGHT_MARGIN = 5; + private static int DAY_HEADER_ONE_DAY_BOTTOM_MARGIN = 6; + private static int DAY_HEADER_RIGHT_MARGIN = 4; + private static int DAY_HEADER_BOTTOM_MARGIN = 3; + private static float DAY_HEADER_FONT_SIZE = 14; + private static float DATE_HEADER_FONT_SIZE = 32; + private static float NORMAL_FONT_SIZE = 12; + private static float EVENT_TEXT_FONT_SIZE = 12; + private static float HOURS_TEXT_SIZE = 12; + private static float AMPM_TEXT_SIZE = 9; + private static int MIN_HOURS_WIDTH = 96; + private static int MIN_CELL_WIDTH_FOR_TEXT = 20; + private static final int MAX_EVENT_TEXT_LEN = 500; + // smallest height to draw an event with + private static float MIN_EVENT_HEIGHT = 24.0F; // in pixels + private static int CALENDAR_COLOR_SQUARE_SIZE = 10; + private static int EVENT_RECT_TOP_MARGIN = 1; + private static int EVENT_RECT_BOTTOM_MARGIN = 0; + private static int EVENT_RECT_LEFT_MARGIN = 1; + private static int EVENT_RECT_RIGHT_MARGIN = 0; + private static int EVENT_RECT_STROKE_WIDTH = 2; + private static int EVENT_TEXT_TOP_MARGIN = 2; + private static int EVENT_TEXT_BOTTOM_MARGIN = 2; + private static int EVENT_TEXT_LEFT_MARGIN = 6; + private static int EVENT_TEXT_RIGHT_MARGIN = 6; + private static int ALL_DAY_EVENT_RECT_BOTTOM_MARGIN = 1; + private static int EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN; + private static int EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_BOTTOM_MARGIN; + private static int EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN; + private static int EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_RIGHT_MARGIN; + // margins and sizing for the expand allday icon + private static int EXPAND_ALL_DAY_BOTTOM_MARGIN = 10; + // sizing for "box +n" in allDay events + private static int EVENT_SQUARE_WIDTH = 10; + private static int EVENT_LINE_PADDING = 4; + private static int NEW_EVENT_HINT_FONT_SIZE = 12; + + private static int mEventTextColor; + private static int mMoreEventsTextColor; + + private static int mWeek_saturdayColor; + private static int mWeek_sundayColor; + private static int mCalendarDateBannerTextColor; + private static int mCalendarAmPmLabel; + private static int mCalendarGridAreaSelected; + private static int mCalendarGridLineInnerHorizontalColor; + private static int mCalendarGridLineInnerVerticalColor; + private static int mFutureBgColor; + private static int mFutureBgColorRes; + private static int mBgColor; + private static int mNewEventHintColor; + private static int mCalendarHourLabelColor; + private static int mMoreAlldayEventsTextAlpha = MORE_EVENTS_MAX_ALPHA; + + private float mAnimationDistance = 0; + private int mViewStartX; + private int mViewStartY; + private int mMaxViewStartY; + private int mViewHeight; + private int mViewWidth; + private int mGridAreaHeight = -1; + private static int mCellHeight = 0; // shared among all DayViews + private static int mMinCellHeight = 32; + private int mScrollStartY; + private int mPreviousDirection; + private static int mScaledPagingTouchSlop = 0; + + /** + * Vertical distance or span between the two touch points at the start of a + * scaling gesture + */ + private float mStartingSpanY = 0; + /** Height of 1 hour in pixels at the start of a scaling gesture */ + private int mCellHeightBeforeScaleGesture; + /** The hour at the center two touch points */ + private float mGestureCenterHour = 0; + + private boolean mRecalCenterHour = false; + + /** + * Flag to decide whether to handle the up event. Cases where up events + * should be ignored are 1) right after a scale gesture and 2) finger was + * down before app launch + */ + private boolean mHandleActionUp = true; + + private int mHoursTextHeight; + /** + * The height of the area used for allday events + */ + private int mAlldayHeight; + /** + * The height of the allday event area used during animation + */ + private int mAnimateDayHeight = 0; + /** + * The height of an individual allday event during animation + */ + private int mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; + /** + * Whether to use the expand or collapse icon. + */ + private static boolean mUseExpandIcon = true; + /** + * The height of the day names/numbers + */ + private static int DAY_HEADER_HEIGHT = 45; + /** + * The height of the day names/numbers for multi-day views + */ + private static int MULTI_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT; + /** + * The height of the day names/numbers when viewing a single day + */ + private static int ONE_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT; + /** + * Max of all day events in a given day in this view. + */ + private int mMaxAlldayEvents; + /** + * A count of the number of allday events that were not drawn for each day + */ + private int[] mSkippedAlldayEvents; + /** + * The number of allDay events at which point we start hiding allDay events. + */ + private int mMaxUnexpandedAlldayEventCount = 4; + /** + * Whether or not to expand the allDay area to fill the screen + */ + private static boolean mShowAllAllDayEvents = false; + + protected int mNumDays = 7; + private int mNumHours = 10; + + /** Width of the time line (list of hours) to the left. */ + private int mHoursWidth; + private int mDateStrWidth; + /** Top of the scrollable region i.e. below date labels and all day events */ + private int mFirstCell; + /** First fully visibile hour */ + private int mFirstHour = -1; + /** Distance between the mFirstCell and the top of first fully visible hour. */ + private int mFirstHourOffset; + private String[] mHourStrs; + private String[] mDayStrs; + private String[] mDayStrs2Letter; + private boolean mIs24HourFormat; + + private final ArrayList mSelectedEvents = new ArrayList(); + private boolean mComputeSelectedEvents; + private boolean mUpdateToast; + private Event mSelectedEvent; + private Event mPrevSelectedEvent; + private final Rect mPrevBox = new Rect(); + protected final Resources mResources; + protected final Drawable mCurrentTimeLine; + protected final Drawable mCurrentTimeAnimateLine; + protected final Drawable mTodayHeaderDrawable; + protected final Drawable mExpandAlldayDrawable; + protected final Drawable mCollapseAlldayDrawable; + protected Drawable mAcceptedOrTentativeEventBoxDrawable; + private String mAmString; + private String mPmString; + private static int sCounter = 0; + + ScaleGestureDetector mScaleGestureDetector; + + /** + * The initial state of the touch mode when we enter this view. + */ + private static final int TOUCH_MODE_INITIAL_STATE = 0; + + /** + * Indicates we just received the touch event and we are waiting to see if + * it is a tap or a scroll gesture. + */ + private static final int TOUCH_MODE_DOWN = 1; + + /** + * Indicates the touch gesture is a vertical scroll + */ + private static final int TOUCH_MODE_VSCROLL = 0x20; + + /** + * Indicates the touch gesture is a horizontal scroll + */ + private static final int TOUCH_MODE_HSCROLL = 0x40; + + private int mTouchMode = TOUCH_MODE_INITIAL_STATE; + + /** + * The selection modes are HIDDEN, PRESSED, SELECTED, and LONGPRESS. + */ + private static final int SELECTION_HIDDEN = 0; + private static final int SELECTION_PRESSED = 1; // D-pad down but not up yet + private static final int SELECTION_SELECTED = 2; + private static final int SELECTION_LONGPRESS = 3; + + private int mSelectionMode = SELECTION_HIDDEN; + + private boolean mScrolling = false; + + // Pixels scrolled + private float mInitialScrollX; + private float mInitialScrollY; + + private boolean mAnimateToday = false; + private int mAnimateTodayAlpha = 0; + + // Animates the height of the allday region + ObjectAnimator mAlldayAnimator; + // Animates the height of events in the allday region + ObjectAnimator mAlldayEventAnimator; + // Animates the transparency of the more events text + ObjectAnimator mMoreAlldayEventsAnimator; + // Animates the current time marker when Today is pressed + ObjectAnimator mTodayAnimator; + // whether or not an event is stopping because it was cancelled + private boolean mCancellingAnimations = false; + // tracks whether a touch originated in the allday area + private boolean mTouchStartedInAlldayArea = false; + + private final CalendarController mController; + private final ViewSwitcher mViewSwitcher; + private final GestureDetector mGestureDetector; + private final OverScroller mScroller; + private final EdgeEffect mEdgeEffectTop; + private final EdgeEffect mEdgeEffectBottom; + private boolean mCallEdgeEffectOnAbsorb; + private final int OVERFLING_DISTANCE; + private float mLastVelocity; + + private final ScrollInterpolator mHScrollInterpolator; + private AccessibilityManager mAccessibilityMgr = null; + private boolean mIsAccessibilityEnabled = false; + private boolean mTouchExplorationEnabled = false; + private final String mNewEventHintString; + + public DayView(Context context, CalendarController controller, + ViewSwitcher viewSwitcher, EventLoader eventLoader, int numDays) { + super(context); + mContext = context; + initAccessibilityVariables(); + + mResources = context.getResources(); + mNewEventHintString = mResources.getString(R.string.day_view_new_event_hint); + mNumDays = numDays; + + DATE_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.date_header_text_size); + DAY_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.day_label_text_size); + ONE_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.one_day_header_height); + DAY_HEADER_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.day_header_bottom_margin); + EXPAND_ALL_DAY_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.all_day_bottom_margin); + HOURS_TEXT_SIZE = (int) mResources.getDimension(R.dimen.hours_text_size); + AMPM_TEXT_SIZE = (int) mResources.getDimension(R.dimen.ampm_text_size); + MIN_HOURS_WIDTH = (int) mResources.getDimension(R.dimen.min_hours_width); + HOURS_LEFT_MARGIN = (int) mResources.getDimension(R.dimen.hours_left_margin); + HOURS_RIGHT_MARGIN = (int) mResources.getDimension(R.dimen.hours_right_margin); + MULTI_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.day_header_height); + int eventTextSizeId; + if (mNumDays == 1) { + eventTextSizeId = R.dimen.day_view_event_text_size; + } else { + eventTextSizeId = R.dimen.week_view_event_text_size; + } + EVENT_TEXT_FONT_SIZE = (int) mResources.getDimension(eventTextSizeId); + NEW_EVENT_HINT_FONT_SIZE = (int) mResources.getDimension(R.dimen.new_event_hint_text_size); + MIN_EVENT_HEIGHT = mResources.getDimension(R.dimen.event_min_height); + MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = MIN_EVENT_HEIGHT; + EVENT_TEXT_TOP_MARGIN = (int) mResources.getDimension(R.dimen.event_text_vertical_margin); + EVENT_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN; + EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN; + EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN; + + EVENT_TEXT_LEFT_MARGIN = (int) mResources + .getDimension(R.dimen.event_text_horizontal_margin); + EVENT_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN; + EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN; + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN; + + if (mScale == 0) { + + mScale = mResources.getDisplayMetrics().density; + if (mScale != 1) { + SINGLE_ALLDAY_HEIGHT *= mScale; + ALLDAY_TOP_MARGIN *= mScale; + MAX_HEIGHT_OF_ONE_ALLDAY_EVENT *= mScale; + + NORMAL_FONT_SIZE *= mScale; + GRID_LINE_LEFT_MARGIN *= mScale; + HOURS_TOP_MARGIN *= mScale; + MIN_CELL_WIDTH_FOR_TEXT *= mScale; + MAX_UNEXPANDED_ALLDAY_HEIGHT *= mScale; + mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; + + CURRENT_TIME_LINE_SIDE_BUFFER *= mScale; + CURRENT_TIME_LINE_TOP_OFFSET *= mScale; + + MIN_Y_SPAN *= mScale; + MAX_CELL_HEIGHT *= mScale; + DEFAULT_CELL_HEIGHT *= mScale; + DAY_HEADER_HEIGHT *= mScale; + DAY_HEADER_RIGHT_MARGIN *= mScale; + DAY_HEADER_ONE_DAY_LEFT_MARGIN *= mScale; + DAY_HEADER_ONE_DAY_RIGHT_MARGIN *= mScale; + DAY_HEADER_ONE_DAY_BOTTOM_MARGIN *= mScale; + CALENDAR_COLOR_SQUARE_SIZE *= mScale; + EVENT_RECT_TOP_MARGIN *= mScale; + EVENT_RECT_BOTTOM_MARGIN *= mScale; + ALL_DAY_EVENT_RECT_BOTTOM_MARGIN *= mScale; + EVENT_RECT_LEFT_MARGIN *= mScale; + EVENT_RECT_RIGHT_MARGIN *= mScale; + EVENT_RECT_STROKE_WIDTH *= mScale; + EVENT_SQUARE_WIDTH *= mScale; + EVENT_LINE_PADDING *= mScale; + NEW_EVENT_MARGIN *= mScale; + NEW_EVENT_WIDTH *= mScale; + NEW_EVENT_MAX_LENGTH *= mScale; + } + } + HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN; + DAY_HEADER_HEIGHT = mNumDays == 1 ? ONE_DAY_HEADER_HEIGHT : MULTI_DAY_HEADER_HEIGHT; + + mCurrentTimeLine = mResources.getDrawable(R.drawable.timeline_indicator_holo_light); + mCurrentTimeAnimateLine = mResources + .getDrawable(R.drawable.timeline_indicator_activated_holo_light); + mTodayHeaderDrawable = mResources.getDrawable(R.drawable.today_blue_week_holo_light); + mExpandAlldayDrawable = mResources.getDrawable(R.drawable.ic_expand_holo_light); + mCollapseAlldayDrawable = mResources.getDrawable(R.drawable.ic_collapse_holo_light); + mNewEventHintColor = mResources.getColor(R.color.new_event_hint_text_color); + mAcceptedOrTentativeEventBoxDrawable = mResources + .getDrawable(R.drawable.panel_month_event_holo_light); + + mEventLoader = eventLoader; + mEventGeometry = new EventGeometry(); + mEventGeometry.setMinEventHeight(MIN_EVENT_HEIGHT); + mEventGeometry.setHourGap(HOUR_GAP); + mEventGeometry.setCellMargin(DAY_GAP); + mLastPopupEventID = INVALID_EVENT_ID; + mController = controller; + mViewSwitcher = viewSwitcher; + mGestureDetector = new GestureDetector(context, new CalendarGestureListener()); + mScaleGestureDetector = new ScaleGestureDetector(getContext(), this); + if (mCellHeight == 0) { + mCellHeight = Utils.getSharedPreference(mContext, + GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, DEFAULT_CELL_HEIGHT); + } + mScroller = new OverScroller(context); + mHScrollInterpolator = new ScrollInterpolator(); + mEdgeEffectTop = new EdgeEffect(context); + mEdgeEffectBottom = new EdgeEffect(context); + ViewConfiguration vc = ViewConfiguration.get(context); + mScaledPagingTouchSlop = vc.getScaledPagingTouchSlop(); + mOnDownDelay = ViewConfiguration.getTapTimeout(); + OVERFLING_DISTANCE = vc.getScaledOverflingDistance(); + + init(context); + } + + @Override + protected void onAttachedToWindow() { + if (mHandler == null) { + mHandler = getHandler(); + mHandler.post(mUpdateCurrentTime); + } + } + + private void init(Context context) { + setFocusable(true); + + // Allow focus in touch mode so that we can do keyboard shortcuts + // even after we've entered touch mode. + setFocusableInTouchMode(true); + setClickable(true); + setOnCreateContextMenuListener(this); + + mFirstDayOfWeek = Utils.getFirstDayOfWeek(context); + + mCurrentTime = new Time(Utils.getTimeZone(context, mTZUpdater)); + long currentTime = System.currentTimeMillis(); + mCurrentTime.set(currentTime); + mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff); + + mWeek_saturdayColor = mResources.getColor(R.color.week_saturday); + mWeek_sundayColor = mResources.getColor(R.color.week_sunday); + mCalendarDateBannerTextColor = mResources.getColor(R.color.calendar_date_banner_text_color); + mFutureBgColorRes = mResources.getColor(R.color.calendar_future_bg_color); + mBgColor = mResources.getColor(R.color.calendar_hour_background); + mCalendarAmPmLabel = mResources.getColor(R.color.calendar_ampm_label); + mCalendarGridAreaSelected = mResources.getColor(R.color.calendar_grid_area_selected); + mCalendarGridLineInnerHorizontalColor = mResources + .getColor(R.color.calendar_grid_line_inner_horizontal_color); + mCalendarGridLineInnerVerticalColor = mResources + .getColor(R.color.calendar_grid_line_inner_vertical_color); + mCalendarHourLabelColor = mResources.getColor(R.color.calendar_hour_label); + mEventTextColor = mResources.getColor(R.color.calendar_event_text_color); + mMoreEventsTextColor = mResources.getColor(R.color.month_event_other_color); + + mEventTextPaint.setTextSize(EVENT_TEXT_FONT_SIZE); + mEventTextPaint.setTextAlign(Paint.Align.LEFT); + mEventTextPaint.setAntiAlias(true); + + int gridLineColor = mResources.getColor(R.color.calendar_grid_line_highlight_color); + Paint p = mSelectionPaint; + p.setColor(gridLineColor); + p.setStyle(Style.FILL); + p.setAntiAlias(false); + + p = mPaint; + p.setAntiAlias(true); + + // Allocate space for 2 weeks worth of weekday names so that we can + // easily start the week display at any week day. + mDayStrs = new String[14]; + + // Also create an array of 2-letter abbreviations. + mDayStrs2Letter = new String[14]; + + for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { + int index = i - Calendar.SUNDAY; + // e.g. Tue for Tuesday + mDayStrs[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_MEDIUM) + .toUpperCase(); + mDayStrs[index + 7] = mDayStrs[index]; + // e.g. Tu for Tuesday + mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORT) + .toUpperCase(); + + // If we don't have 2-letter day strings, fall back to 1-letter. + if (mDayStrs2Letter[index].equals(mDayStrs[index])) { + mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORTEST); + } + + mDayStrs2Letter[index + 7] = mDayStrs2Letter[index]; + } + + // Figure out how much space we need for the 3-letter abbrev names + // in the worst case. + p.setTextSize(DATE_HEADER_FONT_SIZE); + p.setTypeface(mBold); + String[] dateStrs = {" 28", " 30"}; + mDateStrWidth = computeMaxStringWidth(0, dateStrs, p); + p.setTextSize(DAY_HEADER_FONT_SIZE); + mDateStrWidth += computeMaxStringWidth(0, mDayStrs, p); + + p.setTextSize(HOURS_TEXT_SIZE); + p.setTypeface(null); + handleOnResume(); + + mAmString = DateUtils.getAMPMString(Calendar.AM).toUpperCase(); + mPmString = DateUtils.getAMPMString(Calendar.PM).toUpperCase(); + String[] ampm = {mAmString, mPmString}; + p.setTextSize(AMPM_TEXT_SIZE); + mHoursWidth = Math.max(HOURS_MARGIN, computeMaxStringWidth(mHoursWidth, ampm, p) + + HOURS_RIGHT_MARGIN); + mHoursWidth = Math.max(MIN_HOURS_WIDTH, mHoursWidth); + + LayoutInflater inflater; + inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mPopupView = inflater.inflate(R.layout.bubble_event, null); + mPopupView.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + mPopup = new PopupWindow(context); + mPopup.setContentView(mPopupView); + Resources.Theme dialogTheme = getResources().newTheme(); + dialogTheme.applyStyle(android.R.style.Theme_Dialog, true); + TypedArray ta = dialogTheme.obtainStyledAttributes(new int[] { + android.R.attr.windowBackground }); + mPopup.setBackgroundDrawable(ta.getDrawable(0)); + ta.recycle(); + + // Enable touching the popup window + mPopupView.setOnClickListener(this); + // Catch long clicks for creating a new event + setOnLongClickListener(this); + + mBaseDate = new Time(Utils.getTimeZone(context, mTZUpdater)); + long millis = System.currentTimeMillis(); + mBaseDate.set(millis); + + mEarliestStartHour = new int[mNumDays]; + mHasAllDayEvent = new boolean[mNumDays]; + + // mLines is the array of points used with Canvas.drawLines() in + // drawGridBackground() and drawAllDayEvents(). Its size depends + // on the max number of lines that can ever be drawn by any single + // drawLines() call in either of those methods. + final int maxGridLines = (24 + 1) // max horizontal lines we might draw + + (mNumDays + 1); // max vertical lines we might draw + mLines = new float[maxGridLines * 4]; + } + + /** + * This is called when the popup window is pressed. + */ + public void onClick(View v) { + if (v == mPopupView) { + // Pretend it was a trackball click because that will always + // jump to the "View event" screen. + switchViews(true /* trackball */); + } + } + + public void handleOnResume() { + initAccessibilityVariables(); + if(Utils.getSharedPreference(mContext, OtherPreferences.KEY_OTHER_1, false)) { + mFutureBgColor = 0; + } else { + mFutureBgColor = mFutureBgColorRes; + } + mIs24HourFormat = DateFormat.is24HourFormat(mContext); + mHourStrs = mIs24HourFormat ? CalendarData.s24Hours : CalendarData.s12HoursNoAmPm; + mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); + mLastSelectionDayForAccessibility = 0; + mLastSelectionHourForAccessibility = 0; + mLastSelectedEventForAccessibility = null; + mSelectionMode = SELECTION_HIDDEN; + } + + private void initAccessibilityVariables() { + mAccessibilityMgr = (AccessibilityManager) mContext + .getSystemService(Service.ACCESSIBILITY_SERVICE); + mIsAccessibilityEnabled = mAccessibilityMgr != null && mAccessibilityMgr.isEnabled(); + mTouchExplorationEnabled = isTouchExplorationEnabled(); + } + + /** + * Returns the start of the selected time in milliseconds since the epoch. + * + * @return selected time in UTC milliseconds since the epoch. + */ + long getSelectedTimeInMillis() { + Time time = new Time(mBaseDate); + time.setJulianDay(mSelectionDay); + time.hour = mSelectionHour; + + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + return time.normalize(true /* ignore isDst */); + } + + Time getSelectedTime() { + Time time = new Time(mBaseDate); + time.setJulianDay(mSelectionDay); + time.hour = mSelectionHour; + + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + time.normalize(true /* ignore isDst */); + return time; + } + + Time getSelectedTimeForAccessibility() { + Time time = new Time(mBaseDate); + time.setJulianDay(mSelectionDayForAccessibility); + time.hour = mSelectionHourForAccessibility; + + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + time.normalize(true /* ignore isDst */); + return time; + } + + /** + * Returns the start of the selected time in minutes since midnight, + * local time. The derived class must ensure that this is consistent + * with the return value from getSelectedTimeInMillis(). + */ + int getSelectedMinutesSinceMidnight() { + return mSelectionHour * MINUTES_PER_HOUR; + } + + int getFirstVisibleHour() { + return mFirstHour; + } + + void setFirstVisibleHour(int firstHour) { + mFirstHour = firstHour; + mFirstHourOffset = 0; + } + + public void setSelected(Time time, boolean ignoreTime, boolean animateToday) { + mBaseDate.set(time); + setSelectedHour(mBaseDate.hour); + setSelectedEvent(null); + mPrevSelectedEvent = null; + long millis = mBaseDate.toMillis(false /* use isDst */); + setSelectedDay(Time.getJulianDay(millis, mBaseDate.gmtoff)); + mSelectedEvents.clear(); + mComputeSelectedEvents = true; + + int gotoY = Integer.MIN_VALUE; + + if (!ignoreTime && mGridAreaHeight != -1) { + int lastHour = 0; + + if (mBaseDate.hour < mFirstHour) { + // Above visible region + gotoY = mBaseDate.hour * (mCellHeight + HOUR_GAP); + } else { + lastHour = (mGridAreaHeight - mFirstHourOffset) / (mCellHeight + HOUR_GAP) + + mFirstHour; + + if (mBaseDate.hour >= lastHour) { + // Below visible region + + // target hour + 1 (to give it room to see the event) - + // grid height (to get the y of the top of the visible + // region) + gotoY = (int) ((mBaseDate.hour + 1 + mBaseDate.minute / 60.0f) + * (mCellHeight + HOUR_GAP) - mGridAreaHeight); + } + } + + if (DEBUG) { + Log.e(TAG, "Go " + gotoY + " 1st " + mFirstHour + ":" + mFirstHourOffset + "CH " + + (mCellHeight + HOUR_GAP) + " lh " + lastHour + " gh " + mGridAreaHeight + + " ymax " + mMaxViewStartY); + } + + if (gotoY > mMaxViewStartY) { + gotoY = mMaxViewStartY; + } else if (gotoY < 0 && gotoY != Integer.MIN_VALUE) { + gotoY = 0; + } + } + + recalc(); + + mRemeasure = true; + invalidate(); + + boolean delayAnimateToday = false; + if (gotoY != Integer.MIN_VALUE) { + ValueAnimator scrollAnim = ObjectAnimator.ofInt(this, "viewStartY", mViewStartY, gotoY); + scrollAnim.setDuration(GOTO_SCROLL_DURATION); + scrollAnim.setInterpolator(new AccelerateDecelerateInterpolator()); + scrollAnim.addListener(mAnimatorListener); + scrollAnim.start(); + delayAnimateToday = true; + } + if (animateToday) { + synchronized (mTodayAnimatorListener) { + if (mTodayAnimator != null) { + mTodayAnimator.removeAllListeners(); + mTodayAnimator.cancel(); + } + mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha", + mAnimateTodayAlpha, 255); + mAnimateToday = true; + mTodayAnimatorListener.setFadingIn(true); + mTodayAnimatorListener.setAnimator(mTodayAnimator); + mTodayAnimator.addListener(mTodayAnimatorListener); + mTodayAnimator.setDuration(150); + if (delayAnimateToday) { + mTodayAnimator.setStartDelay(GOTO_SCROLL_DURATION); + } + mTodayAnimator.start(); + } + } + sendAccessibilityEventAsNeeded(false); + } + + // Called from animation framework via reflection. Do not remove + public void setViewStartY(int viewStartY) { + if (viewStartY > mMaxViewStartY) { + viewStartY = mMaxViewStartY; + } + + mViewStartY = viewStartY; + + computeFirstHour(); + invalidate(); + } + + public void setAnimateTodayAlpha(int todayAlpha) { + mAnimateTodayAlpha = todayAlpha; + invalidate(); + } + + public Time getSelectedDay() { + Time time = new Time(mBaseDate); + time.setJulianDay(mSelectionDay); + time.hour = mSelectionHour; + + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + time.normalize(true /* ignore isDst */); + return time; + } + + public void updateTitle() { + Time start = new Time(mBaseDate); + start.normalize(true); + Time end = new Time(start); + end.monthDay += mNumDays - 1; + // Move it forward one minute so the formatter doesn't lose a day + end.minute += 1; + end.normalize(true); + + long formatFlags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; + if (mNumDays != 1) { + // Don't show day of the month if for multi-day view + formatFlags |= DateUtils.FORMAT_NO_MONTH_DAY; + + // Abbreviate the month if showing multiple months + if (start.month != end.month) { + formatFlags |= DateUtils.FORMAT_ABBREV_MONTH; + } + } + + mController.sendEvent(this, EventType.UPDATE_TITLE, start, end, null, -1, ViewType.CURRENT, + formatFlags, null, null); + } + + /** + * return a negative number if "time" is comes before the visible time + * range, a positive number if "time" is after the visible time range, and 0 + * if it is in the visible time range. + */ + public int compareToVisibleTimeRange(Time time) { + + int savedHour = mBaseDate.hour; + int savedMinute = mBaseDate.minute; + int savedSec = mBaseDate.second; + + mBaseDate.hour = 0; + mBaseDate.minute = 0; + mBaseDate.second = 0; + + if (DEBUG) { + Log.d(TAG, "Begin " + mBaseDate.toString()); + Log.d(TAG, "Diff " + time.toString()); + } + + // Compare beginning of range + int diff = Time.compare(time, mBaseDate); + if (diff > 0) { + // Compare end of range + mBaseDate.monthDay += mNumDays; + mBaseDate.normalize(true); + diff = Time.compare(time, mBaseDate); + + if (DEBUG) Log.d(TAG, "End " + mBaseDate.toString()); + + mBaseDate.monthDay -= mNumDays; + mBaseDate.normalize(true); + if (diff < 0) { + // in visible time + diff = 0; + } else if (diff == 0) { + // Midnight of following day + diff = 1; + } + } + + if (DEBUG) Log.d(TAG, "Diff: " + diff); + + mBaseDate.hour = savedHour; + mBaseDate.minute = savedMinute; + mBaseDate.second = savedSec; + return diff; + } + + private void recalc() { + // Set the base date to the beginning of the week if we are displaying + // 7 days at a time. + if (mNumDays == 7) { + adjustToBeginningOfWeek(mBaseDate); + } + + final long start = mBaseDate.toMillis(false /* use isDst */); + mFirstJulianDay = Time.getJulianDay(start, mBaseDate.gmtoff); + mLastJulianDay = mFirstJulianDay + mNumDays - 1; + + mMonthLength = mBaseDate.getActualMaximum(Time.MONTH_DAY); + mFirstVisibleDate = mBaseDate.monthDay; + mFirstVisibleDayOfWeek = mBaseDate.weekDay; + } + + private void adjustToBeginningOfWeek(Time time) { + int dayOfWeek = time.weekDay; + int diff = dayOfWeek - mFirstDayOfWeek; + if (diff != 0) { + if (diff < 0) { + diff += 7; + } + time.monthDay -= diff; + time.normalize(true /* ignore isDst */); + } + } + + @Override + protected void onSizeChanged(int width, int height, int oldw, int oldh) { + mViewWidth = width; + mViewHeight = height; + mEdgeEffectTop.setSize(mViewWidth, mViewHeight); + mEdgeEffectBottom.setSize(mViewWidth, mViewHeight); + int gridAreaWidth = width - mHoursWidth; + mCellWidth = (gridAreaWidth - (mNumDays * DAY_GAP)) / mNumDays; + + // This would be about 1 day worth in a 7 day view + mHorizontalSnapBackThreshold = width / 7; + + Paint p = new Paint(); + p.setTextSize(HOURS_TEXT_SIZE); + mHoursTextHeight = (int) Math.abs(p.ascent()); + remeasure(width, height); + } + + /** + * Measures the space needed for various parts of the view after + * loading new events. This can change if there are all-day events. + */ + private void remeasure(int width, int height) { + // Shrink to fit available space but make sure we can display at least two events + MAX_UNEXPANDED_ALLDAY_HEIGHT = (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4); + MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.min(MAX_UNEXPANDED_ALLDAY_HEIGHT, height / 6); + MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.max(MAX_UNEXPANDED_ALLDAY_HEIGHT, + (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 2); + mMaxUnexpandedAlldayEventCount = + (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT / MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT); + + // First, clear the array of earliest start times, and the array + // indicating presence of an all-day event. + for (int day = 0; day < mNumDays; day++) { + mEarliestStartHour[day] = 25; // some big number + mHasAllDayEvent[day] = false; + } + + int maxAllDayEvents = mMaxAlldayEvents; + + // The min is where 24 hours cover the entire visible area + mMinCellHeight = Math.max((height - DAY_HEADER_HEIGHT) / 24, (int) MIN_EVENT_HEIGHT); + if (mCellHeight < mMinCellHeight) { + mCellHeight = mMinCellHeight; + } + + // Calculate mAllDayHeight + mFirstCell = DAY_HEADER_HEIGHT; + int allDayHeight = 0; + if (maxAllDayEvents > 0) { + int maxAllAllDayHeight = height - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; + // If there is at most one all-day event per day, then use less + // space (but more than the space for a single event). + if (maxAllDayEvents == 1) { + allDayHeight = SINGLE_ALLDAY_HEIGHT; + } else if (maxAllDayEvents <= mMaxUnexpandedAlldayEventCount){ + // Allow the all-day area to grow in height depending on the + // number of all-day events we need to show, up to a limit. + allDayHeight = maxAllDayEvents * MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; + if (allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) { + allDayHeight = MAX_UNEXPANDED_ALLDAY_HEIGHT; + } + } else { + // if we have more than the magic number, check if we're animating + // and if not adjust the sizes appropriately + if (mAnimateDayHeight != 0) { + // Don't shrink the space past the final allDay space. The animation + // continues to hide the last event so the more events text can + // fade in. + allDayHeight = Math.max(mAnimateDayHeight, MAX_UNEXPANDED_ALLDAY_HEIGHT); + } else { + // Try to fit all the events in + allDayHeight = (int) (maxAllDayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT); + // But clip the area depending on which mode we're in + if (!mShowAllAllDayEvents && allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) { + allDayHeight = (int) (mMaxUnexpandedAlldayEventCount * + MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT); + } else if (allDayHeight > maxAllAllDayHeight) { + allDayHeight = maxAllAllDayHeight; + } + } + } + mFirstCell = DAY_HEADER_HEIGHT + allDayHeight + ALLDAY_TOP_MARGIN; + } else { + mSelectionAllday = false; + } + mAlldayHeight = allDayHeight; + + mGridAreaHeight = height - mFirstCell; + + // Set up the expand icon position + int allDayIconWidth = mExpandAlldayDrawable.getIntrinsicWidth(); + mExpandAllDayRect.left = Math.max((mHoursWidth - allDayIconWidth) / 2, + EVENT_ALL_DAY_TEXT_LEFT_MARGIN); + mExpandAllDayRect.right = Math.min(mExpandAllDayRect.left + allDayIconWidth, mHoursWidth + - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN); + mExpandAllDayRect.bottom = mFirstCell - EXPAND_ALL_DAY_BOTTOM_MARGIN; + mExpandAllDayRect.top = mExpandAllDayRect.bottom + - mExpandAlldayDrawable.getIntrinsicHeight(); + + mNumHours = mGridAreaHeight / (mCellHeight + HOUR_GAP); + mEventGeometry.setHourHeight(mCellHeight); + + final long minimumDurationMillis = (long) + (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f)); + Event.computePositions(mEvents, minimumDurationMillis); + + // Compute the top of our reachable view + mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight; + if (DEBUG) { + Log.e(TAG, "mViewStartY: " + mViewStartY); + Log.e(TAG, "mMaxViewStartY: " + mMaxViewStartY); + } + if (mViewStartY > mMaxViewStartY) { + mViewStartY = mMaxViewStartY; + computeFirstHour(); + } + + if (mFirstHour == -1) { + initFirstHour(); + mFirstHourOffset = 0; + } + + // When we change the base date, the number of all-day events may + // change and that changes the cell height. When we switch dates, + // we use the mFirstHourOffset from the previous view, but that may + // be too large for the new view if the cell height is smaller. + if (mFirstHourOffset >= mCellHeight + HOUR_GAP) { + mFirstHourOffset = mCellHeight + HOUR_GAP - 1; + } + mViewStartY = mFirstHour * (mCellHeight + HOUR_GAP) - mFirstHourOffset; + + final int eventAreaWidth = mNumDays * (mCellWidth + DAY_GAP); + //When we get new events we don't want to dismiss the popup unless the event changes + if (mSelectedEvent != null && mLastPopupEventID != mSelectedEvent.id) { + mPopup.dismiss(); + } + mPopup.setWidth(eventAreaWidth - 20); + mPopup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT); + } + + /** + * Initialize the state for another view. The given view is one that has + * its own bitmap and will use an animation to replace the current view. + * The current view and new view are either both Week views or both Day + * views. They differ in their base date. + * + * @param view the view to initialize. + */ + private void initView(DayView view) { + view.setSelectedHour(mSelectionHour); + view.mSelectedEvents.clear(); + view.mComputeSelectedEvents = true; + view.mFirstHour = mFirstHour; + view.mFirstHourOffset = mFirstHourOffset; + view.remeasure(getWidth(), getHeight()); + view.initAllDayHeights(); + + view.setSelectedEvent(null); + view.mPrevSelectedEvent = null; + view.mFirstDayOfWeek = mFirstDayOfWeek; + if (view.mEvents.size() > 0) { + view.mSelectionAllday = mSelectionAllday; + } else { + view.mSelectionAllday = false; + } + + // Redraw the screen so that the selection box will be redrawn. We may + // have scrolled to a different part of the day in some other view + // so the selection box in this view may no longer be visible. + view.recalc(); + } + + /** + * Switch to another view based on what was selected (an event or a free + * slot) and how it was selected (by touch or by trackball). + * + * @param trackBallSelection true if the selection was made using the + * trackball. + */ + private void switchViews(boolean trackBallSelection) { + Event selectedEvent = mSelectedEvent; + + mPopup.dismiss(); + mLastPopupEventID = INVALID_EVENT_ID; + if (mNumDays > 1) { + // This is the Week view. + // With touch, we always switch to Day/Agenda View + // With track ball, if we selected a free slot, then create an event. + // If we selected a specific event, switch to EventInfo view. + if (trackBallSelection) { + if (selectedEvent != null) { + if (mIsAccessibilityEnabled) { + mAccessibilityMgr.interrupt(); + } + } + } + } + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + mScrolling = false; + return super.onKeyUp(keyCode, event); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return super.onKeyDown(keyCode, event); + } + + + @Override + public boolean onHoverEvent(MotionEvent event) { + return true; + } + + private boolean isTouchExplorationEnabled() { + return mIsAccessibilityEnabled && mAccessibilityMgr.isTouchExplorationEnabled(); + } + + private void sendAccessibilityEventAsNeeded(boolean speakEvents) { + if (!mIsAccessibilityEnabled) { + return; + } + boolean dayChanged = mLastSelectionDayForAccessibility != mSelectionDayForAccessibility; + boolean hourChanged = mLastSelectionHourForAccessibility != mSelectionHourForAccessibility; + if (dayChanged || hourChanged || + mLastSelectedEventForAccessibility != mSelectedEventForAccessibility) { + mLastSelectionDayForAccessibility = mSelectionDayForAccessibility; + mLastSelectionHourForAccessibility = mSelectionHourForAccessibility; + mLastSelectedEventForAccessibility = mSelectedEventForAccessibility; + + StringBuilder b = new StringBuilder(); + + // Announce only the changes i.e. day or hour or both + if (dayChanged) { + b.append(getSelectedTimeForAccessibility().format("%A ")); + } + if (hourChanged) { + b.append(getSelectedTimeForAccessibility().format(mIs24HourFormat ? "%k" : "%l%p")); + } + if (dayChanged || hourChanged) { + b.append(PERIOD_SPACE); + } + + if (speakEvents) { + if (mEventCountTemplate == null) { + mEventCountTemplate = mContext.getString(R.string.template_announce_item_index); + } + + // Read out the relevant event(s) + int numEvents = mSelectedEvents.size(); + if (numEvents > 0) { + if (mSelectedEventForAccessibility == null) { + // Read out all the events + int i = 1; + for (Event calEvent : mSelectedEvents) { + if (numEvents > 1) { + // Read out x of numEvents if there are more than one event + mStringBuilder.setLength(0); + b.append(mFormatter.format(mEventCountTemplate, i++, numEvents)); + b.append(" "); + } + appendEventAccessibilityString(b, calEvent); + } + } else { + if (numEvents > 1) { + // Read out x of numEvents if there are more than one event + mStringBuilder.setLength(0); + b.append(mFormatter.format(mEventCountTemplate, mSelectedEvents + .indexOf(mSelectedEventForAccessibility) + 1, numEvents)); + b.append(" "); + } + appendEventAccessibilityString(b, mSelectedEventForAccessibility); + } + } + } + + if (dayChanged || hourChanged || speakEvents) { + AccessibilityEvent event = AccessibilityEvent + .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED); + CharSequence msg = b.toString(); + event.getText().add(msg); + event.setAddedCount(msg.length()); + sendAccessibilityEventUnchecked(event); + } + } + } + + /** + * @param b + * @param calEvent + */ + private void appendEventAccessibilityString(StringBuilder b, Event calEvent) { + b.append(calEvent.getTitleAndLocation()); + b.append(PERIOD_SPACE); + String when; + int flags = DateUtils.FORMAT_SHOW_DATE; + if (calEvent.allDay) { + flags |= DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_WEEKDAY; + } else { + flags |= DateUtils.FORMAT_SHOW_TIME; + if (DateFormat.is24HourFormat(mContext)) { + flags |= DateUtils.FORMAT_24HOUR; + } + } + when = Utils.formatDateRange(mContext, calEvent.startMillis, calEvent.endMillis, flags); + b.append(when); + b.append(PERIOD_SPACE); + } + + private class GotoBroadcaster implements Animation.AnimationListener { + private final int mCounter; + private final Time mStart; + private final Time mEnd; + + public GotoBroadcaster(Time start, Time end) { + mCounter = ++sCounter; + mStart = start; + mEnd = end; + } + + @Override + public void onAnimationEnd(Animation animation) { + DayView view = (DayView) mViewSwitcher.getCurrentView(); + view.mViewStartX = 0; + view = (DayView) mViewSwitcher.getNextView(); + view.mViewStartX = 0; + + if (mCounter == sCounter) { + mController.sendEvent(this, EventType.GO_TO, mStart, mEnd, null, -1, + ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null); + } + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + + @Override + public void onAnimationStart(Animation animation) { + } + } + + private View switchViews(boolean forward, float xOffSet, float width, float velocity) { + mAnimationDistance = width - xOffSet; + if (DEBUG) { + Log.d(TAG, "switchViews(" + forward + ") O:" + xOffSet + " Dist:" + mAnimationDistance); + } + + float progress = Math.abs(xOffSet) / width; + if (progress > 1.0f) { + progress = 1.0f; + } + + float inFromXValue, inToXValue; + float outFromXValue, outToXValue; + if (forward) { + inFromXValue = 1.0f - progress; + inToXValue = 0.0f; + outFromXValue = -progress; + outToXValue = -1.0f; + } else { + inFromXValue = progress - 1.0f; + inToXValue = 0.0f; + outFromXValue = progress; + outToXValue = 1.0f; + } + + final Time start = new Time(mBaseDate.timezone); + start.set(mController.getTime()); + if (forward) { + start.monthDay += mNumDays; + } else { + start.monthDay -= mNumDays; + } + mController.setTime(start.normalize(true)); + + Time newSelected = start; + + if (mNumDays == 7) { + newSelected = new Time(start); + adjustToBeginningOfWeek(start); + } + + final Time end = new Time(start); + end.monthDay += mNumDays - 1; + + // 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( + Animation.RELATIVE_TO_SELF, inFromXValue, + Animation.RELATIVE_TO_SELF, inToXValue, + Animation.ABSOLUTE, 0.0f, + Animation.ABSOLUTE, 0.0f); + + TranslateAnimation outAnimation = new TranslateAnimation( + Animation.RELATIVE_TO_SELF, outFromXValue, + Animation.RELATIVE_TO_SELF, outToXValue, + Animation.ABSOLUTE, 0.0f, + Animation.ABSOLUTE, 0.0f); + + long duration = calculateDuration(width - Math.abs(xOffSet), width, velocity); + inAnimation.setDuration(duration); + inAnimation.setInterpolator(mHScrollInterpolator); + outAnimation.setInterpolator(mHScrollInterpolator); + outAnimation.setDuration(duration); + outAnimation.setAnimationListener(new GotoBroadcaster(start, end)); + mViewSwitcher.setInAnimation(inAnimation); + mViewSwitcher.setOutAnimation(outAnimation); + + DayView view = (DayView) mViewSwitcher.getCurrentView(); + view.cleanup(); + mViewSwitcher.showNext(); + view = (DayView) mViewSwitcher.getCurrentView(); + view.setSelected(newSelected, true, false); + view.requestFocus(); + view.reloadEvents(); + view.updateTitle(); + view.restartCurrentTimeUpdates(); + + return view; + } + + // This is called after scrolling stops to move the selected hour + // to the visible part of the screen. + private void resetSelectedHour() { + if (mSelectionHour < mFirstHour + 1) { + setSelectedHour(mFirstHour + 1); + setSelectedEvent(null); + mSelectedEvents.clear(); + mComputeSelectedEvents = true; + } else if (mSelectionHour > mFirstHour + mNumHours - 3) { + setSelectedHour(mFirstHour + mNumHours - 3); + setSelectedEvent(null); + mSelectedEvents.clear(); + mComputeSelectedEvents = true; + } + } + + private void initFirstHour() { + mFirstHour = mSelectionHour - mNumHours / 5; + if (mFirstHour < 0) { + mFirstHour = 0; + } else if (mFirstHour + mNumHours > 24) { + mFirstHour = 24 - mNumHours; + } + } + + /** + * Recomputes the first full hour that is visible on screen after the + * screen is scrolled. + */ + private void computeFirstHour() { + // Compute the first full hour that is visible on screen + mFirstHour = (mViewStartY + mCellHeight + HOUR_GAP - 1) / (mCellHeight + HOUR_GAP); + mFirstHourOffset = mFirstHour * (mCellHeight + HOUR_GAP) - mViewStartY; + } + + private void adjustHourSelection() { + if (mSelectionHour < 0) { + setSelectedHour(0); + if (mMaxAlldayEvents > 0) { + mPrevSelectedEvent = null; + mSelectionAllday = true; + } + } + + if (mSelectionHour > 23) { + setSelectedHour(23); + } + + // If the selected hour is at least 2 time slots from the top and + // bottom of the screen, then don't scroll the view. + if (mSelectionHour < mFirstHour + 1) { + // If there are all-days events for the selected day but there + // are no more normal events earlier in the day, then jump to + // the all-day event area. + // Exception 1: allow the user to scroll to 8am with the trackball + // before jumping to the all-day event area. + // Exception 2: if 12am is on screen, then allow the user to select + // 12am before going up to the all-day event area. + int daynum = mSelectionDay - mFirstJulianDay; + if (daynum < mEarliestStartHour.length && daynum >= 0 + && mMaxAlldayEvents > 0 + && mEarliestStartHour[daynum] > mSelectionHour + && mFirstHour > 0 && mFirstHour < 8) { + mPrevSelectedEvent = null; + mSelectionAllday = true; + setSelectedHour(mFirstHour + 1); + return; + } + + if (mFirstHour > 0) { + mFirstHour -= 1; + mViewStartY -= (mCellHeight + HOUR_GAP); + if (mViewStartY < 0) { + mViewStartY = 0; + } + return; + } + } + + if (mSelectionHour > mFirstHour + mNumHours - 3) { + if (mFirstHour < 24 - mNumHours) { + mFirstHour += 1; + mViewStartY += (mCellHeight + HOUR_GAP); + if (mViewStartY > mMaxViewStartY) { + mViewStartY = mMaxViewStartY; + } + return; + } else if (mFirstHour == 24 - mNumHours && mFirstHourOffset > 0) { + mViewStartY = mMaxViewStartY; + } + } + } + + void clearCachedEvents() { + mLastReloadMillis = 0; + } + + private final Runnable mCancelCallback = new Runnable() { + public void run() { + clearCachedEvents(); + } + }; + + /* package */ void reloadEvents() { + // Protect against this being called before this view has been + // initialized. +// if (mContext == null) { +// return; +// } + + // Make sure our time zones are up to date + mTZUpdater.run(); + + setSelectedEvent(null); + mPrevSelectedEvent = null; + mSelectedEvents.clear(); + + // The start date is the beginning of the week at 12am + Time weekStart = new Time(Utils.getTimeZone(mContext, mTZUpdater)); + weekStart.set(mBaseDate); + weekStart.hour = 0; + weekStart.minute = 0; + weekStart.second = 0; + long millis = weekStart.normalize(true /* ignore isDst */); + + // Avoid reloading events unnecessarily. + if (millis == mLastReloadMillis) { + return; + } + mLastReloadMillis = millis; + + // load events in the background +// mContext.startProgressSpinner(); + final ArrayList events = new ArrayList(); + mEventLoader.loadEventsInBackground(mNumDays, events, mFirstJulianDay, new Runnable() { + + public void run() { + boolean fadeinEvents = mFirstJulianDay != mLoadedFirstJulianDay; + mEvents = events; + mLoadedFirstJulianDay = mFirstJulianDay; + if (mAllDayEvents == null) { + mAllDayEvents = new ArrayList(); + } else { + mAllDayEvents.clear(); + } + + // Create a shorter array for all day events + for (Event e : events) { + if (e.drawAsAllday()) { + mAllDayEvents.add(e); + } + } + + // New events, new layouts + if (mLayouts == null || mLayouts.length < events.size()) { + mLayouts = new StaticLayout[events.size()]; + } else { + Arrays.fill(mLayouts, null); + } + + if (mAllDayLayouts == null || mAllDayLayouts.length < mAllDayEvents.size()) { + mAllDayLayouts = new StaticLayout[events.size()]; + } else { + Arrays.fill(mAllDayLayouts, null); + } + + computeEventRelations(); + + mRemeasure = true; + mComputeSelectedEvents = true; + recalc(); + + // Start animation to cross fade the events + if (fadeinEvents) { + if (mEventsCrossFadeAnimation == null) { + mEventsCrossFadeAnimation = + ObjectAnimator.ofInt(DayView.this, "EventsAlpha", 0, 255); + mEventsCrossFadeAnimation.setDuration(EVENTS_CROSS_FADE_DURATION); + } + mEventsCrossFadeAnimation.start(); + } else{ + invalidate(); + } + } + }, mCancelCallback); + } + + public void setEventsAlpha(int alpha) { + mEventsAlpha = alpha; + invalidate(); + } + + public int getEventsAlpha() { + return mEventsAlpha; + } + + public void stopEventsAnimation() { + if (mEventsCrossFadeAnimation != null) { + mEventsCrossFadeAnimation.cancel(); + } + mEventsAlpha = 255; + } + + private void computeEventRelations() { + // Compute the layout relation between each event before measuring cell + // width, as the cell width should be adjusted along with the relation. + // + // Examples: A (1:00pm - 1:01pm), B (1:02pm - 2:00pm) + // We should mark them as "overwapped". Though they are not overwapped logically, but + // minimum cell height implicitly expands the cell height of A and it should look like + // (1:00pm - 1:15pm) after the cell height adjustment. + + // Compute the space needed for the all-day events, if any. + // Make a pass over all the events, and keep track of the maximum + // number of all-day events in any one day. Also, keep track of + // the earliest event in each day. + int maxAllDayEvents = 0; + final ArrayList events = mEvents; + final int len = events.size(); + // Num of all-day-events on each day. + final int eventsCount[] = new int[mLastJulianDay - mFirstJulianDay + 1]; + Arrays.fill(eventsCount, 0); + for (int ii = 0; ii < len; ii++) { + Event event = events.get(ii); + if (event.startDay > mLastJulianDay || event.endDay < mFirstJulianDay) { + continue; + } + if (event.drawAsAllday()) { + // Count all the events being drawn as allDay events + final int firstDay = Math.max(event.startDay, mFirstJulianDay); + final int lastDay = Math.min(event.endDay, mLastJulianDay); + for (int day = firstDay; day <= lastDay; day++) { + final int count = ++eventsCount[day - mFirstJulianDay]; + if (maxAllDayEvents < count) { + maxAllDayEvents = count; + } + } + + int daynum = event.startDay - mFirstJulianDay; + int durationDays = event.endDay - event.startDay + 1; + if (daynum < 0) { + durationDays += daynum; + daynum = 0; + } + if (daynum + durationDays > mNumDays) { + durationDays = mNumDays - daynum; + } + for (int day = daynum; durationDays > 0; day++, durationDays--) { + mHasAllDayEvent[day] = true; + } + } else { + int daynum = event.startDay - mFirstJulianDay; + int hour = event.startTime / 60; + if (daynum >= 0 && hour < mEarliestStartHour[daynum]) { + mEarliestStartHour[daynum] = hour; + } + + // Also check the end hour in case the event spans more than + // one day. + daynum = event.endDay - mFirstJulianDay; + hour = event.endTime / 60; + if (daynum < mNumDays && hour < mEarliestStartHour[daynum]) { + mEarliestStartHour[daynum] = hour; + } + } + } + mMaxAlldayEvents = maxAllDayEvents; + initAllDayHeights(); + } + + @Override + protected void onDraw(Canvas canvas) { + if (mRemeasure) { + remeasure(getWidth(), getHeight()); + mRemeasure = false; + } + canvas.save(); + + float yTranslate = -mViewStartY + DAY_HEADER_HEIGHT + mAlldayHeight; + // offset canvas by the current drag and header position + canvas.translate(-mViewStartX, yTranslate); + // clip to everything below the allDay area + Rect dest = mDestRect; + dest.top = (int) (mFirstCell - yTranslate); + dest.bottom = (int) (mViewHeight - yTranslate); + dest.left = 0; + dest.right = mViewWidth; + canvas.save(); + canvas.clipRect(dest); + // Draw the movable part of the view + doDraw(canvas); + // restore to having no clip + canvas.restore(); + + if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { + float xTranslate; + if (mViewStartX > 0) { + xTranslate = mViewWidth; + } else { + xTranslate = -mViewWidth; + } + // Move the canvas around to prep it for the next view + // specifically, shift it by a screen and undo the + // yTranslation which will be redone in the nextView's onDraw(). + canvas.translate(xTranslate, -yTranslate); + DayView nextView = (DayView) mViewSwitcher.getNextView(); + + // Prevent infinite recursive calls to onDraw(). + nextView.mTouchMode = TOUCH_MODE_INITIAL_STATE; + + nextView.onDraw(canvas); + // Move it back for this view + canvas.translate(-xTranslate, 0); + } else { + // If we drew another view we already translated it back + // If we didn't draw another view we should be at the edge of the + // screen + canvas.translate(mViewStartX, -yTranslate); + } + + // Draw the fixed areas (that don't scroll) directly to the canvas. + drawAfterScroll(canvas); + if (mComputeSelectedEvents && mUpdateToast) { + mUpdateToast = false; + } + mComputeSelectedEvents = false; + + // Draw overscroll glow + if (!mEdgeEffectTop.isFinished()) { + if (DAY_HEADER_HEIGHT != 0) { + canvas.translate(0, DAY_HEADER_HEIGHT); + } + if (mEdgeEffectTop.draw(canvas)) { + invalidate(); + } + if (DAY_HEADER_HEIGHT != 0) { + canvas.translate(0, -DAY_HEADER_HEIGHT); + } + } + if (!mEdgeEffectBottom.isFinished()) { + canvas.rotate(180, mViewWidth/2, mViewHeight/2); + if (mEdgeEffectBottom.draw(canvas)) { + invalidate(); + } + } + canvas.restore(); + } + + private void drawAfterScroll(Canvas canvas) { + Paint p = mPaint; + Rect r = mRect; + + drawAllDayHighlights(r, canvas, p); + if (mMaxAlldayEvents != 0) { + drawAllDayEvents(mFirstJulianDay, mNumDays, canvas, p); + drawUpperLeftCorner(r, canvas, p); + } + + drawScrollLine(r, canvas, p); + drawDayHeaderLoop(r, canvas, p); + + // Draw the AM and PM indicators if we're in 12 hour mode + if (!mIs24HourFormat) { + drawAmPm(canvas, p); + } + } + + // This isn't really the upper-left corner. It's the square area just + // below the upper-left corner, above the hours and to the left of the + // all-day area. + private void drawUpperLeftCorner(Rect r, Canvas canvas, Paint p) { + setupHourTextPaint(p); + if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { + // Draw the allDay expand/collapse icon + if (mUseExpandIcon) { + mExpandAlldayDrawable.setBounds(mExpandAllDayRect); + mExpandAlldayDrawable.draw(canvas); + } else { + mCollapseAlldayDrawable.setBounds(mExpandAllDayRect); + mCollapseAlldayDrawable.draw(canvas); + } + } + } + + private void drawScrollLine(Rect r, Canvas canvas, Paint p) { + final int right = computeDayLeftPosition(mNumDays); + final int y = mFirstCell - 1; + + p.setAntiAlias(false); + p.setStyle(Style.FILL); + + p.setColor(mCalendarGridLineInnerHorizontalColor); + p.setStrokeWidth(GRID_LINE_INNER_WIDTH); + canvas.drawLine(GRID_LINE_LEFT_MARGIN, y, right, y, p); + p.setAntiAlias(true); + } + + // Computes the x position for the left side of the given day (base 0) + private int computeDayLeftPosition(int day) { + int effectiveWidth = mViewWidth - mHoursWidth; + return day * effectiveWidth / mNumDays + mHoursWidth; + } + + private void drawAllDayHighlights(Rect r, Canvas canvas, Paint p) { + if (mFutureBgColor != 0) { + // First, color the labels area light gray + r.top = 0; + r.bottom = DAY_HEADER_HEIGHT; + r.left = 0; + r.right = mViewWidth; + p.setColor(mBgColor); + p.setStyle(Style.FILL); + canvas.drawRect(r, p); + // and the area that says All day + r.top = DAY_HEADER_HEIGHT; + r.bottom = mFirstCell - 1; + r.left = 0; + r.right = mHoursWidth; + canvas.drawRect(r, p); + + int startIndex = -1; + + int todayIndex = mTodayJulianDay - mFirstJulianDay; + if (todayIndex < 0) { + // Future + startIndex = 0; + } else if (todayIndex >= 1 && todayIndex + 1 < mNumDays) { + // Multiday - tomorrow is visible. + startIndex = todayIndex + 1; + } + + if (startIndex >= 0) { + // Draw the future highlight + r.top = 0; + r.bottom = mFirstCell - 1; + r.left = computeDayLeftPosition(startIndex) + 1; + r.right = computeDayLeftPosition(mNumDays); + p.setColor(mFutureBgColor); + p.setStyle(Style.FILL); + canvas.drawRect(r, p); + } + } + } + + private void drawDayHeaderLoop(Rect r, Canvas canvas, Paint p) { + // Draw the horizontal day background banner + // p.setColor(mCalendarDateBannerBackground); + // r.top = 0; + // r.bottom = DAY_HEADER_HEIGHT; + // r.left = 0; + // r.right = mHoursWidth + mNumDays * (mCellWidth + DAY_GAP); + // canvas.drawRect(r, p); + // + // Fill the extra space on the right side with the default background + // r.left = r.right; + // r.right = mViewWidth; + // p.setColor(mCalendarGridAreaBackground); + // canvas.drawRect(r, p); + if (mNumDays == 1 && ONE_DAY_HEADER_HEIGHT == 0) { + return; + } + + p.setTypeface(mBold); + p.setTextAlign(Paint.Align.RIGHT); + int cell = mFirstJulianDay; + + String[] dayNames; + if (mDateStrWidth < mCellWidth) { + dayNames = mDayStrs; + } else { + dayNames = mDayStrs2Letter; + } + + p.setAntiAlias(true); + for (int day = 0; day < mNumDays; day++, cell++) { + int dayOfWeek = day + mFirstVisibleDayOfWeek; + if (dayOfWeek >= 14) { + dayOfWeek -= 14; + } + + int color = mCalendarDateBannerTextColor; + if (mNumDays == 1) { + if (dayOfWeek == Time.SATURDAY) { + color = mWeek_saturdayColor; + } else if (dayOfWeek == Time.SUNDAY) { + color = mWeek_sundayColor; + } + } else { + final int column = day % 7; + if (Utils.isSaturday(column, mFirstDayOfWeek)) { + color = mWeek_saturdayColor; + } else if (Utils.isSunday(column, mFirstDayOfWeek)) { + color = mWeek_sundayColor; + } + } + + p.setColor(color); + drawDayHeader(dayNames[dayOfWeek], day, cell, canvas, p); + } + p.setTypeface(null); + } + + private void drawAmPm(Canvas canvas, Paint p) { + p.setColor(mCalendarAmPmLabel); + p.setTextSize(AMPM_TEXT_SIZE); + p.setTypeface(mBold); + p.setAntiAlias(true); + p.setTextAlign(Paint.Align.RIGHT); + String text = mAmString; + if (mFirstHour >= 12) { + text = mPmString; + } + int y = mFirstCell + mFirstHourOffset + 2 * mHoursTextHeight + HOUR_GAP; + canvas.drawText(text, HOURS_LEFT_MARGIN, y, p); + + if (mFirstHour < 12 && mFirstHour + mNumHours > 12) { + // Also draw the "PM" + text = mPmString; + y = mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP) + + 2 * mHoursTextHeight + HOUR_GAP; + canvas.drawText(text, HOURS_LEFT_MARGIN, y, p); + } + } + + private void drawCurrentTimeLine(Rect r, final int day, final int top, Canvas canvas, + Paint p) { + r.left = computeDayLeftPosition(day) - CURRENT_TIME_LINE_SIDE_BUFFER + 1; + r.right = computeDayLeftPosition(day + 1) + CURRENT_TIME_LINE_SIDE_BUFFER + 1; + + r.top = top - CURRENT_TIME_LINE_TOP_OFFSET; + r.bottom = r.top + mCurrentTimeLine.getIntrinsicHeight(); + + mCurrentTimeLine.setBounds(r); + mCurrentTimeLine.draw(canvas); + if (mAnimateToday) { + mCurrentTimeAnimateLine.setBounds(r); + mCurrentTimeAnimateLine.setAlpha(mAnimateTodayAlpha); + mCurrentTimeAnimateLine.draw(canvas); + } + } + + private void doDraw(Canvas canvas) { + Paint p = mPaint; + Rect r = mRect; + + if (mFutureBgColor != 0) { + drawBgColors(r, canvas, p); + } + drawGridBackground(r, canvas, p); + drawHours(r, canvas, p); + + // Draw each day + int cell = mFirstJulianDay; + p.setAntiAlias(false); + int alpha = p.getAlpha(); + p.setAlpha(mEventsAlpha); + for (int day = 0; day < mNumDays; day++, cell++) { + // TODO Wow, this needs cleanup. drawEvents loop through all the + // events on every call. + drawEvents(cell, day, HOUR_GAP, canvas, p); + // If this is today + if (cell == mTodayJulianDay) { + int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP) + + ((mCurrentTime.minute * mCellHeight) / 60) + 1; + + // And the current time shows up somewhere on the screen + if (lineY >= mViewStartY && lineY < mViewStartY + mViewHeight - 2) { + drawCurrentTimeLine(r, day, lineY, canvas, p); + } + } + } + p.setAntiAlias(true); + p.setAlpha(alpha); + } + + private void drawHours(Rect r, Canvas canvas, Paint p) { + setupHourTextPaint(p); + + int y = HOUR_GAP + mHoursTextHeight + HOURS_TOP_MARGIN; + + for (int i = 0; i < 24; i++) { + String time = mHourStrs[i]; + canvas.drawText(time, HOURS_LEFT_MARGIN, y, p); + y += mCellHeight + HOUR_GAP; + } + } + + private void setupHourTextPaint(Paint p) { + p.setColor(mCalendarHourLabelColor); + p.setTextSize(HOURS_TEXT_SIZE); + p.setTypeface(Typeface.DEFAULT); + p.setTextAlign(Paint.Align.RIGHT); + p.setAntiAlias(true); + } + + private void drawDayHeader(String dayStr, int day, int cell, Canvas canvas, Paint p) { + int dateNum = mFirstVisibleDate + day; + int x; + if (dateNum > mMonthLength) { + dateNum -= mMonthLength; + } + p.setAntiAlias(true); + + int todayIndex = mTodayJulianDay - mFirstJulianDay; + // Draw day of the month + String dateNumStr = String.valueOf(dateNum); + if (mNumDays > 1) { + float y = DAY_HEADER_HEIGHT - DAY_HEADER_BOTTOM_MARGIN; + + // Draw day of the month + x = computeDayLeftPosition(day + 1) - DAY_HEADER_RIGHT_MARGIN; + p.setTextAlign(Align.RIGHT); + p.setTextSize(DATE_HEADER_FONT_SIZE); + + p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT); + canvas.drawText(dateNumStr, x, y, p); + + // Draw day of the week + x -= p.measureText(" " + dateNumStr); + p.setTextSize(DAY_HEADER_FONT_SIZE); + p.setTypeface(Typeface.DEFAULT); + canvas.drawText(dayStr, x, y, p); + } else { + float y = ONE_DAY_HEADER_HEIGHT - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN; + p.setTextAlign(Align.LEFT); + + + // Draw day of the week + x = computeDayLeftPosition(day) + DAY_HEADER_ONE_DAY_LEFT_MARGIN; + p.setTextSize(DAY_HEADER_FONT_SIZE); + p.setTypeface(Typeface.DEFAULT); + canvas.drawText(dayStr, x, y, p); + + // Draw day of the month + x += p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN; + p.setTextSize(DATE_HEADER_FONT_SIZE); + p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT); + canvas.drawText(dateNumStr, x, y, p); + } + } + + private void drawGridBackground(Rect r, Canvas canvas, Paint p) { + Paint.Style savedStyle = p.getStyle(); + + final float stopX = computeDayLeftPosition(mNumDays); + float y = 0; + final float deltaY = mCellHeight + HOUR_GAP; + int linesIndex = 0; + final float startY = 0; + final float stopY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP); + float x = mHoursWidth; + + // Draw the inner horizontal grid lines + p.setColor(mCalendarGridLineInnerHorizontalColor); + p.setStrokeWidth(GRID_LINE_INNER_WIDTH); + p.setAntiAlias(false); + y = 0; + linesIndex = 0; + for (int hour = 0; hour <= 24; hour++) { + mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN; + mLines[linesIndex++] = y; + mLines[linesIndex++] = stopX; + mLines[linesIndex++] = y; + y += deltaY; + } + if (mCalendarGridLineInnerVerticalColor != mCalendarGridLineInnerHorizontalColor) { + canvas.drawLines(mLines, 0, linesIndex, p); + linesIndex = 0; + p.setColor(mCalendarGridLineInnerVerticalColor); + } + + // Draw the inner vertical grid lines + for (int day = 0; day <= mNumDays; day++) { + x = computeDayLeftPosition(day); + mLines[linesIndex++] = x; + mLines[linesIndex++] = startY; + mLines[linesIndex++] = x; + mLines[linesIndex++] = stopY; + } + canvas.drawLines(mLines, 0, linesIndex, p); + + // Restore the saved style. + p.setStyle(savedStyle); + p.setAntiAlias(true); + } + + /** + * @param r + * @param canvas + * @param p + */ + private void drawBgColors(Rect r, Canvas canvas, Paint p) { + int todayIndex = mTodayJulianDay - mFirstJulianDay; + // Draw the hours background color + r.top = mDestRect.top; + r.bottom = mDestRect.bottom; + r.left = 0; + r.right = mHoursWidth; + p.setColor(mBgColor); + p.setStyle(Style.FILL); + p.setAntiAlias(false); + canvas.drawRect(r, p); + + // Draw background for grid area + if (mNumDays == 1 && todayIndex == 0) { + // Draw a white background for the time later than current time + int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP) + + ((mCurrentTime.minute * mCellHeight) / 60) + 1; + if (lineY < mViewStartY + mViewHeight) { + lineY = Math.max(lineY, mViewStartY); + r.left = mHoursWidth; + r.right = mViewWidth; + r.top = lineY; + r.bottom = mViewStartY + mViewHeight; + p.setColor(mFutureBgColor); + canvas.drawRect(r, p); + } + } else if (todayIndex >= 0 && todayIndex < mNumDays) { + // Draw today with a white background for the time later than current time + int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP) + + ((mCurrentTime.minute * mCellHeight) / 60) + 1; + if (lineY < mViewStartY + mViewHeight) { + lineY = Math.max(lineY, mViewStartY); + r.left = computeDayLeftPosition(todayIndex) + 1; + r.right = computeDayLeftPosition(todayIndex + 1); + r.top = lineY; + r.bottom = mViewStartY + mViewHeight; + p.setColor(mFutureBgColor); + canvas.drawRect(r, p); + } + + // Paint Tomorrow and later days with future color + if (todayIndex + 1 < mNumDays) { + r.left = computeDayLeftPosition(todayIndex + 1) + 1; + r.right = computeDayLeftPosition(mNumDays); + r.top = mDestRect.top; + r.bottom = mDestRect.bottom; + p.setColor(mFutureBgColor); + canvas.drawRect(r, p); + } + } else if (todayIndex < 0) { + // Future + r.left = computeDayLeftPosition(0) + 1; + r.right = computeDayLeftPosition(mNumDays); + r.top = mDestRect.top; + r.bottom = mDestRect.bottom; + p.setColor(mFutureBgColor); + canvas.drawRect(r, p); + } + p.setAntiAlias(true); + } + + private int computeMaxStringWidth(int currentMax, String[] strings, Paint p) { + float maxWidthF = 0.0f; + + int len = strings.length; + for (int i = 0; i < len; i++) { + float width = p.measureText(strings[i]); + maxWidthF = Math.max(width, maxWidthF); + } + int maxWidth = (int) (maxWidthF + 0.5); + if (maxWidth < currentMax) { + maxWidth = currentMax; + } + return maxWidth; + } + + private void saveSelectionPosition(float left, float top, float right, float bottom) { + mPrevBox.left = (int) left; + mPrevBox.right = (int) right; + mPrevBox.top = (int) top; + mPrevBox.bottom = (int) bottom; + } + + private void setupTextRect(Rect r) { + if (r.bottom <= r.top || r.right <= r.left) { + r.bottom = r.top; + r.right = r.left; + return; + } + + if (r.bottom - r.top > EVENT_TEXT_TOP_MARGIN + EVENT_TEXT_BOTTOM_MARGIN) { + r.top += EVENT_TEXT_TOP_MARGIN; + r.bottom -= EVENT_TEXT_BOTTOM_MARGIN; + } + if (r.right - r.left > EVENT_TEXT_LEFT_MARGIN + EVENT_TEXT_RIGHT_MARGIN) { + r.left += EVENT_TEXT_LEFT_MARGIN; + r.right -= EVENT_TEXT_RIGHT_MARGIN; + } + } + + private void setupAllDayTextRect(Rect r) { + if (r.bottom <= r.top || r.right <= r.left) { + r.bottom = r.top; + r.right = r.left; + return; + } + + if (r.bottom - r.top > EVENT_ALL_DAY_TEXT_TOP_MARGIN + EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN) { + r.top += EVENT_ALL_DAY_TEXT_TOP_MARGIN; + r.bottom -= EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN; + } + if (r.right - r.left > EVENT_ALL_DAY_TEXT_LEFT_MARGIN + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN) { + r.left += EVENT_ALL_DAY_TEXT_LEFT_MARGIN; + r.right -= EVENT_ALL_DAY_TEXT_RIGHT_MARGIN; + } + } + + /** + * Return the layout for a numbered event. Create it if not already existing + */ + private StaticLayout getEventLayout(StaticLayout[] layouts, int i, Event event, Paint paint, + Rect r) { + if (i < 0 || i >= layouts.length) { + return null; + } + + StaticLayout layout = layouts[i]; + // Check if we have already initialized the StaticLayout and that + // the width hasn't changed (due to vertical resizing which causes + // re-layout of events at min height) + if (layout == null || r.width() != layout.getWidth()) { + SpannableStringBuilder bob = new SpannableStringBuilder(); + if (event.title != null) { + // MAX - 1 since we add a space + bob.append(drawTextSanitizer(event.title.toString(), MAX_EVENT_TEXT_LEN - 1)); + bob.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length(), 0); + bob.append(' '); + } + if (event.location != null) { + bob.append(drawTextSanitizer(event.location.toString(), + MAX_EVENT_TEXT_LEN - bob.length())); + } + + switch (event.selfAttendeeStatus) { + case Attendees.ATTENDEE_STATUS_INVITED: + paint.setColor(event.color); + break; + case Attendees.ATTENDEE_STATUS_DECLINED: + paint.setColor(mEventTextColor); + paint.setAlpha(Utils.DECLINED_EVENT_TEXT_ALPHA); + break; + case Attendees.ATTENDEE_STATUS_NONE: // Your own events + case Attendees.ATTENDEE_STATUS_ACCEPTED: + case Attendees.ATTENDEE_STATUS_TENTATIVE: + default: + paint.setColor(mEventTextColor); + break; + } + + // Leave a one pixel boundary on the left and right of the rectangle for the event + layout = new StaticLayout(bob, 0, bob.length(), new TextPaint(paint), r.width(), + Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null, r.width()); + + layouts[i] = layout; + } + layout.getPaint().setAlpha(mEventsAlpha); + return layout; + } + + private void drawAllDayEvents(int firstDay, int numDays, Canvas canvas, Paint p) { + + p.setTextSize(NORMAL_FONT_SIZE); + p.setTextAlign(Paint.Align.LEFT); + Paint eventTextPaint = mEventTextPaint; + + final float startY = DAY_HEADER_HEIGHT; + final float stopY = startY + mAlldayHeight + ALLDAY_TOP_MARGIN; + float x = 0; + int linesIndex = 0; + + // Draw the inner vertical grid lines + p.setColor(mCalendarGridLineInnerVerticalColor); + x = mHoursWidth; + p.setStrokeWidth(GRID_LINE_INNER_WIDTH); + // Line bounding the top of the all day area + mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN; + mLines[linesIndex++] = startY; + mLines[linesIndex++] = computeDayLeftPosition(mNumDays); + mLines[linesIndex++] = startY; + + for (int day = 0; day <= mNumDays; day++) { + x = computeDayLeftPosition(day); + mLines[linesIndex++] = x; + mLines[linesIndex++] = startY; + mLines[linesIndex++] = x; + mLines[linesIndex++] = stopY; + } + p.setAntiAlias(false); + canvas.drawLines(mLines, 0, linesIndex, p); + p.setStyle(Style.FILL); + + int y = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN; + int lastDay = firstDay + numDays - 1; + final ArrayList events = mAllDayEvents; + int numEvents = events.size(); + // Whether or not we should draw the more events text + boolean hasMoreEvents = false; + // size of the allDay area + float drawHeight = mAlldayHeight; + // max number of events being drawn in one day of the allday area + float numRectangles = mMaxAlldayEvents; + // Where to cut off drawn allday events + int allDayEventClip = DAY_HEADER_HEIGHT + mAlldayHeight + ALLDAY_TOP_MARGIN; + // The number of events that weren't drawn in each day + mSkippedAlldayEvents = new int[numDays]; + if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount && !mShowAllAllDayEvents && + mAnimateDayHeight == 0) { + // We draw one fewer event than will fit so that more events text + // can be drawn + numRectangles = mMaxUnexpandedAlldayEventCount - 1; + // We also clip the events above the more events text + allDayEventClip -= MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; + hasMoreEvents = true; + } else if (mAnimateDayHeight != 0) { + // clip at the end of the animating space + allDayEventClip = DAY_HEADER_HEIGHT + mAnimateDayHeight + ALLDAY_TOP_MARGIN; + } + + int alpha = eventTextPaint.getAlpha(); + eventTextPaint.setAlpha(mEventsAlpha); + for (int i = 0; i < numEvents; i++) { + Event event = events.get(i); + int startDay = event.startDay; + int endDay = event.endDay; + if (startDay > lastDay || endDay < firstDay) { + continue; + } + if (startDay < firstDay) { + startDay = firstDay; + } + if (endDay > lastDay) { + endDay = lastDay; + } + int startIndex = startDay - firstDay; + int endIndex = endDay - firstDay; + float height = mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount ? mAnimateDayEventHeight : + drawHeight / numRectangles; + + // Prevent a single event from getting too big + if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) { + height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; + } + + // Leave a one-pixel space between the vertical day lines and the + // event rectangle. + event.left = computeDayLeftPosition(startIndex); + event.right = computeDayLeftPosition(endIndex + 1) - DAY_GAP; + event.top = y + height * event.getColumn(); + event.bottom = event.top + height - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN; + if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { + // check if we should skip this event. We skip if it starts + // after the clip bound or ends after the skip bound and we're + // not animating. + if (event.top >= allDayEventClip) { + incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex); + continue; + } else if (event.bottom > allDayEventClip) { + if (hasMoreEvents) { + incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex); + continue; + } + event.bottom = allDayEventClip; + } + } + Rect r = drawEventRect(event, canvas, p, eventTextPaint, (int) event.top, + (int) event.bottom); + setupAllDayTextRect(r); + StaticLayout layout = getEventLayout(mAllDayLayouts, i, event, eventTextPaint, r); + drawEventText(layout, r, canvas, r.top, r.bottom, true); + + // Check if this all-day event intersects the selected day + if (mSelectionAllday && mComputeSelectedEvents) { + if (startDay <= mSelectionDay && endDay >= mSelectionDay) { + mSelectedEvents.add(event); + } + } + } + eventTextPaint.setAlpha(alpha); + + if (mMoreAlldayEventsTextAlpha != 0 && mSkippedAlldayEvents != null) { + // If the more allday text should be visible, draw it. + alpha = p.getAlpha(); + p.setAlpha(mEventsAlpha); + p.setColor(mMoreAlldayEventsTextAlpha << 24 & mMoreEventsTextColor); + for (int i = 0; i < mSkippedAlldayEvents.length; i++) { + if (mSkippedAlldayEvents[i] > 0) { + drawMoreAlldayEvents(canvas, mSkippedAlldayEvents[i], i, p); + } + } + p.setAlpha(alpha); + } + + if (mSelectionAllday) { + // Compute the neighbors for the list of all-day events that + // intersect the selected day. + computeAllDayNeighbors(); + + // Set the selection position to zero so that when we move down + // to the normal event area, we will highlight the topmost event. + saveSelectionPosition(0f, 0f, 0f, 0f); + } + } + + // Helper method for counting the number of allday events skipped on each day + private void incrementSkipCount(int[] counts, int startIndex, int endIndex) { + if (counts == null || startIndex < 0 || endIndex > counts.length) { + return; + } + for (int i = startIndex; i <= endIndex; i++) { + counts[i]++; + } + } + + // Draws the "box +n" text for hidden allday events + protected void drawMoreAlldayEvents(Canvas canvas, int remainingEvents, int day, Paint p) { + int x = computeDayLeftPosition(day) + EVENT_ALL_DAY_TEXT_LEFT_MARGIN; + int y = (int) (mAlldayHeight - .5f * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - .5f + * EVENT_SQUARE_WIDTH + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN); + Rect r = mRect; + r.top = y; + r.left = x; + r.bottom = y + EVENT_SQUARE_WIDTH; + r.right = x + EVENT_SQUARE_WIDTH; + p.setColor(mMoreEventsTextColor); + p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH); + p.setStyle(Style.STROKE); + p.setAntiAlias(false); + canvas.drawRect(r, p); + p.setAntiAlias(true); + p.setStyle(Style.FILL); + p.setTextSize(EVENT_TEXT_FONT_SIZE); + String text = mResources.getQuantityString(R.plurals.month_more_events, remainingEvents); + y += EVENT_SQUARE_WIDTH; + x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING; + canvas.drawText(String.format(text, remainingEvents), x, y, p); + } + + private void computeAllDayNeighbors() { + int len = mSelectedEvents.size(); + if (len == 0 || mSelectedEvent != null) { + return; + } + + // First, clear all the links + for (int ii = 0; ii < len; ii++) { + Event ev = mSelectedEvents.get(ii); + ev.nextUp = null; + ev.nextDown = null; + ev.nextLeft = null; + ev.nextRight = null; + } + + // For each event in the selected event list "mSelectedEvents", find + // its neighbors in the up and down directions. This could be done + // more efficiently by sorting on the Event.getColumn() field, but + // the list is expected to be very small. + + // Find the event in the same row as the previously selected all-day + // event, if any. + int startPosition = -1; + if (mPrevSelectedEvent != null && mPrevSelectedEvent.drawAsAllday()) { + startPosition = mPrevSelectedEvent.getColumn(); + } + int maxPosition = -1; + Event startEvent = null; + Event maxPositionEvent = null; + for (int ii = 0; ii < len; ii++) { + Event ev = mSelectedEvents.get(ii); + int position = ev.getColumn(); + if (position == startPosition) { + startEvent = ev; + } else if (position > maxPosition) { + maxPositionEvent = ev; + maxPosition = position; + } + for (int jj = 0; jj < len; jj++) { + if (jj == ii) { + continue; + } + Event neighbor = mSelectedEvents.get(jj); + int neighborPosition = neighbor.getColumn(); + if (neighborPosition == position - 1) { + ev.nextUp = neighbor; + } else if (neighborPosition == position + 1) { + ev.nextDown = neighbor; + } + } + } + if (startEvent != null) { + setSelectedEvent(startEvent); + } else { + setSelectedEvent(maxPositionEvent); + } + } + + private void drawEvents(int date, int dayIndex, int top, Canvas canvas, Paint p) { + Paint eventTextPaint = mEventTextPaint; + int left = computeDayLeftPosition(dayIndex) + 1; + int cellWidth = computeDayLeftPosition(dayIndex + 1) - left + 1; + int cellHeight = mCellHeight; + + // Use the selected hour as the selection region + Rect selectionArea = mSelectionRect; + selectionArea.top = top + mSelectionHour * (cellHeight + HOUR_GAP); + selectionArea.bottom = selectionArea.top + cellHeight; + selectionArea.left = left; + selectionArea.right = selectionArea.left + cellWidth; + + final ArrayList events = mEvents; + int numEvents = events.size(); + EventGeometry geometry = mEventGeometry; + + final int viewEndY = mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight; + + int alpha = eventTextPaint.getAlpha(); + eventTextPaint.setAlpha(mEventsAlpha); + for (int i = 0; i < numEvents; i++) { + Event event = events.get(i); + if (!geometry.computeEventRect(date, left, top, cellWidth, event)) { + continue; + } + + // Don't draw it if it is not visible + if (event.bottom < mViewStartY || event.top > viewEndY) { + continue; + } + + if (date == mSelectionDay && !mSelectionAllday && mComputeSelectedEvents + && geometry.eventIntersectsSelection(event, selectionArea)) { + mSelectedEvents.add(event); + } + + Rect r = drawEventRect(event, canvas, p, eventTextPaint, mViewStartY, viewEndY); + setupTextRect(r); + + // Don't draw text if it is not visible + if (r.top > viewEndY || r.bottom < mViewStartY) { + continue; + } + StaticLayout layout = getEventLayout(mLayouts, i, event, eventTextPaint, r); + // TODO: not sure why we are 4 pixels off + drawEventText(layout, r, canvas, mViewStartY + 4, mViewStartY + mViewHeight + - DAY_HEADER_HEIGHT - mAlldayHeight, false); + } + eventTextPaint.setAlpha(alpha); + } + + private Rect drawEventRect(Event event, Canvas canvas, Paint p, Paint eventTextPaint, + int visibleTop, int visibleBot) { + // Draw the Event Rect + Rect r = mRect; + r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN, visibleTop); + r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN, visibleBot); + r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN; + r.right = (int) event.right; + + int color = event.color; + switch (event.selfAttendeeStatus) { + case Attendees.ATTENDEE_STATUS_INVITED: + if (event != mClickedEvent) { + p.setStyle(Style.STROKE); + } + break; + case Attendees.ATTENDEE_STATUS_DECLINED: + if (event != mClickedEvent) { + color = Utils.getDeclinedColorFromColor(color); + } + case Attendees.ATTENDEE_STATUS_NONE: // Your own events + case Attendees.ATTENDEE_STATUS_ACCEPTED: + case Attendees.ATTENDEE_STATUS_TENTATIVE: + default: + p.setStyle(Style.FILL_AND_STROKE); + break; + } + + p.setAntiAlias(false); + + int floorHalfStroke = (int) Math.floor(EVENT_RECT_STROKE_WIDTH / 2.0f); + int ceilHalfStroke = (int) Math.ceil(EVENT_RECT_STROKE_WIDTH / 2.0f); + r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN + floorHalfStroke, visibleTop); + r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN - ceilHalfStroke, + visibleBot); + r.left += floorHalfStroke; + r.right -= ceilHalfStroke; + p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH); + p.setColor(color); + int alpha = p.getAlpha(); + p.setAlpha(mEventsAlpha); + canvas.drawRect(r, p); + p.setAlpha(alpha); + p.setStyle(Style.FILL); + + // Setup rect for drawEventText which follows + r.top = (int) event.top + EVENT_RECT_TOP_MARGIN; + r.bottom = (int) event.bottom - EVENT_RECT_BOTTOM_MARGIN; + r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN; + r.right = (int) event.right - EVENT_RECT_RIGHT_MARGIN; + return r; + } + + private final Pattern drawTextSanitizerFilter = Pattern.compile("[\t\n],"); + + // Sanitize a string before passing it to drawText or else we get little + // squares. For newlines and tabs before a comma, delete the character. + // Otherwise, just replace them with a space. + private String drawTextSanitizer(String string, int maxEventTextLen) { + Matcher m = drawTextSanitizerFilter.matcher(string); + string = m.replaceAll(","); + + int len = string.length(); + if (maxEventTextLen <= 0) { + string = ""; + len = 0; + } else if (len > maxEventTextLen) { + string = string.substring(0, maxEventTextLen); + len = maxEventTextLen; + } + + return string.replace('\n', ' '); + } + + private void drawEventText(StaticLayout eventLayout, Rect rect, Canvas canvas, int top, + int bottom, boolean center) { + // drawEmptyRect(canvas, rect, 0xFFFF00FF); // for debugging + + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + // If the rectangle is too small for text, then return + if (eventLayout == null || width < MIN_CELL_WIDTH_FOR_TEXT) { + return; + } + + int totalLineHeight = 0; + int lineCount = eventLayout.getLineCount(); + for (int i = 0; i < lineCount; i++) { + int lineBottom = eventLayout.getLineBottom(i); + if (lineBottom <= height) { + totalLineHeight = lineBottom; + } else { + break; + } + } + + // + 2 is small workaround when the font is slightly bigger then the rect. This will + // still allow the text to be shown without overflowing into the other all day rects. + if (totalLineHeight == 0 || rect.top > bottom || rect.top + totalLineHeight + 2 < top) { + return; + } + + // Use a StaticLayout to format the string. + canvas.save(); + // canvas.translate(rect.left, rect.top + (rect.bottom - rect.top / 2)); + int padding = center? (rect.bottom - rect.top - totalLineHeight) / 2 : 0; + canvas.translate(rect.left, rect.top + padding); + rect.left = 0; + rect.right = width; + rect.top = 0; + rect.bottom = totalLineHeight; + + // There's a bug somewhere. If this rect is outside of a previous + // cliprect, this becomes a no-op. What happens is that the text draw + // past the event rect. The current fix is to not draw the staticLayout + // at all if it is completely out of bound. + canvas.clipRect(rect); + eventLayout.draw(canvas); + canvas.restore(); + } + + // The following routines are called from the parent activity when certain + // touch events occur. + private void doDown(MotionEvent ev) { + mTouchMode = TOUCH_MODE_DOWN; + mViewStartX = 0; + mOnFlingCalled = false; + mHandler.removeCallbacks(mContinueScroll); + int x = (int) ev.getX(); + int y = (int) ev.getY(); + + // Save selection information: we use setSelectionFromPosition to find the selected event + // in order to show the "clicked" color. But since it is also setting the selected info + // for new events, we need to restore the old info after calling the function. + Event oldSelectedEvent = mSelectedEvent; + int oldSelectionDay = mSelectionDay; + int oldSelectionHour = mSelectionHour; + if (setSelectionFromPosition(x, y, false)) { + // If a time was selected (a blue selection box is visible) and the click location + // is in the selected time, do not show a click on an event to prevent a situation + // of both a selection and an event are clicked when they overlap. + boolean pressedSelected = (mSelectionMode != SELECTION_HIDDEN) + && oldSelectionDay == mSelectionDay && oldSelectionHour == mSelectionHour; + if (!pressedSelected && mSelectedEvent != null) { + mSavedClickedEvent = mSelectedEvent; + mDownTouchTime = System.currentTimeMillis(); + postDelayed (mSetClick,mOnDownDelay); + } else { + eventClickCleanup(); + } + } + mSelectedEvent = oldSelectedEvent; + mSelectionDay = oldSelectionDay; + mSelectionHour = oldSelectionHour; + invalidate(); + } + + // Kicks off all the animations when the expand allday area is tapped + private void doExpandAllDayClick() { + mShowAllAllDayEvents = !mShowAllAllDayEvents; + + ObjectAnimator.setFrameDelay(0); + + // Determine the starting height + if (mAnimateDayHeight == 0) { + mAnimateDayHeight = mShowAllAllDayEvents ? + mAlldayHeight - (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT : mAlldayHeight; + } + // Cancel current animations + mCancellingAnimations = true; + if (mAlldayAnimator != null) { + mAlldayAnimator.cancel(); + } + if (mAlldayEventAnimator != null) { + mAlldayEventAnimator.cancel(); + } + if (mMoreAlldayEventsAnimator != null) { + mMoreAlldayEventsAnimator.cancel(); + } + mCancellingAnimations = false; + // get new animators + mAlldayAnimator = getAllDayAnimator(); + mAlldayEventAnimator = getAllDayEventAnimator(); + mMoreAlldayEventsAnimator = ObjectAnimator.ofInt(this, + "moreAllDayEventsTextAlpha", + mShowAllAllDayEvents ? MORE_EVENTS_MAX_ALPHA : 0, + mShowAllAllDayEvents ? 0 : MORE_EVENTS_MAX_ALPHA); + + // Set up delays and start the animators + mAlldayAnimator.setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0); + mAlldayAnimator.start(); + mMoreAlldayEventsAnimator.setStartDelay(mShowAllAllDayEvents ? 0 : ANIMATION_DURATION); + mMoreAlldayEventsAnimator.setDuration(ANIMATION_SECONDARY_DURATION); + mMoreAlldayEventsAnimator.start(); + if (mAlldayEventAnimator != null) { + // This is the only animator that can return null, so check it + mAlldayEventAnimator + .setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0); + mAlldayEventAnimator.start(); + } + } + + /** + * Figures out the initial heights for allDay events and space when + * a view is being set up. + */ + public void initAllDayHeights() { + if (mMaxAlldayEvents <= mMaxUnexpandedAlldayEventCount) { + return; + } + if (mShowAllAllDayEvents) { + int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; + maxADHeight = Math.min(maxADHeight, + (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)); + mAnimateDayEventHeight = maxADHeight / mMaxAlldayEvents; + } else { + mAnimateDayEventHeight = (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; + } + } + + // Sets up an animator for changing the height of allday events + private ObjectAnimator getAllDayEventAnimator() { + // First calculate the absolute max height + int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; + // Now expand to fit but not beyond the absolute max + maxADHeight = + Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)); + // calculate the height of individual events in order to fit + int fitHeight = maxADHeight / mMaxAlldayEvents; + int currentHeight = mAnimateDayEventHeight; + int desiredHeight = + mShowAllAllDayEvents ? fitHeight : (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; + // if there's nothing to animate just return + if (currentHeight == desiredHeight) { + return null; + } + + // Set up the animator with the calculated values + ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayEventHeight", + currentHeight, desiredHeight); + animator.setDuration(ANIMATION_DURATION); + return animator; + } + + // Sets up an animator for changing the height of the allday area + private ObjectAnimator getAllDayAnimator() { + // Calculate the absolute max height + int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; + // Find the desired height but don't exceed abs max + maxADHeight = + Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)); + // calculate the current and desired heights + int currentHeight = mAnimateDayHeight != 0 ? mAnimateDayHeight : mAlldayHeight; + int desiredHeight = mShowAllAllDayEvents ? maxADHeight : + (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - 1); + + // Set up the animator with the calculated values + ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayHeight", + currentHeight, desiredHeight); + animator.setDuration(ANIMATION_DURATION); + + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!mCancellingAnimations) { + // when finished, set this to 0 to signify not animating + mAnimateDayHeight = 0; + mUseExpandIcon = !mShowAllAllDayEvents; + } + mRemeasure = true; + invalidate(); + } + }); + return animator; + } + + // setter for the 'box +n' alpha text used by the animator + public void setMoreAllDayEventsTextAlpha(int alpha) { + mMoreAlldayEventsTextAlpha = alpha; + invalidate(); + } + + // setter for the height of the allday area used by the animator + public void setAnimateDayHeight(int height) { + mAnimateDayHeight = height; + mRemeasure = true; + invalidate(); + } + + // setter for the height of allday events used by the animator + public void setAnimateDayEventHeight(int height) { + mAnimateDayEventHeight = height; + mRemeasure = true; + invalidate(); + } + + private void doSingleTapUp(MotionEvent ev) { + if (!mHandleActionUp || mScrolling) { + return; + } + + int x = (int) ev.getX(); + int y = (int) ev.getY(); + int selectedDay = mSelectionDay; + int selectedHour = mSelectionHour; + + if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { + // check if the tap was in the allday expansion area + int bottom = mFirstCell; + if((x < mHoursWidth && y > DAY_HEADER_HEIGHT && y < DAY_HEADER_HEIGHT + mAlldayHeight) + || (!mShowAllAllDayEvents && mAnimateDayHeight == 0 && y < bottom && + y >= bottom - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)) { + doExpandAllDayClick(); + return; + } + } + + boolean validPosition = setSelectionFromPosition(x, y, false); + if (!validPosition) { + if (y < DAY_HEADER_HEIGHT) { + Time selectedTime = new Time(mBaseDate); + selectedTime.setJulianDay(mSelectionDay); + selectedTime.hour = mSelectionHour; + selectedTime.normalize(true /* ignore isDst */); + mController.sendEvent(this, EventType.GO_TO, null, null, selectedTime, -1, + ViewType.DAY, CalendarController.EXTRA_GOTO_DATE, null, null); + } + return; + } + + boolean hasSelection = mSelectionMode != SELECTION_HIDDEN; + boolean pressedSelected = (hasSelection || mTouchExplorationEnabled) + && selectedDay == mSelectionDay && selectedHour == mSelectionHour; + + if (mSelectedEvent != null) { + // If the tap is on an event, launch the "View event" view + if (mIsAccessibilityEnabled) { + mAccessibilityMgr.interrupt(); + } + + mSelectionMode = SELECTION_HIDDEN; + + int yLocation = + (int)((mSelectedEvent.top + mSelectedEvent.bottom)/2); + // Y location is affected by the position of the event in the scrolling + // view (mViewStartY) and the presence of all day events (mFirstCell) + if (!mSelectedEvent.allDay) { + yLocation += (mFirstCell - mViewStartY); + } + mClickedYLocation = yLocation; + long clearDelay = (CLICK_DISPLAY_DURATION + mOnDownDelay) - + (System.currentTimeMillis() - mDownTouchTime); + if (clearDelay > 0) { + this.postDelayed(mClearClick, clearDelay); + } else { + this.post(mClearClick); + } + } + invalidate(); + } + + private void doLongPress(MotionEvent ev) { + eventClickCleanup(); + if (mScrolling) { + return; + } + + // Scale gesture in progress + if (mStartingSpanY != 0) { + return; + } + + int x = (int) ev.getX(); + int y = (int) ev.getY(); + + boolean validPosition = setSelectionFromPosition(x, y, false); + if (!validPosition) { + // return if the touch wasn't on an area of concern + return; + } + + invalidate(); + performLongClick(); + } + + private void doScroll(MotionEvent e1, MotionEvent e2, float deltaX, float deltaY) { + cancelAnimation(); + if (mStartingScroll) { + mInitialScrollX = 0; + mInitialScrollY = 0; + mStartingScroll = false; + } + + mInitialScrollX += deltaX; + mInitialScrollY += deltaY; + int distanceX = (int) mInitialScrollX; + int distanceY = (int) mInitialScrollY; + + final float focusY = getAverageY(e2); + if (mRecalCenterHour) { + // Calculate the hour that correspond to the average of the Y touch points + mGestureCenterHour = (mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) + / (mCellHeight + DAY_GAP); + mRecalCenterHour = false; + } + + // If we haven't figured out the predominant scroll direction yet, + // then do it now. + if (mTouchMode == TOUCH_MODE_DOWN) { + int absDistanceX = Math.abs(distanceX); + int absDistanceY = Math.abs(distanceY); + mScrollStartY = mViewStartY; + mPreviousDirection = 0; + + if (absDistanceX > absDistanceY) { + int slopFactor = mScaleGestureDetector.isInProgress() ? 20 : 2; + if (absDistanceX > mScaledPagingTouchSlop * slopFactor) { + mTouchMode = TOUCH_MODE_HSCROLL; + mViewStartX = distanceX; + initNextView(-mViewStartX); + } + } else { + mTouchMode = TOUCH_MODE_VSCROLL; + } + } else if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { + // We are already scrolling horizontally, so check if we + // changed the direction of scrolling so that the other week + // is now visible. + mViewStartX = distanceX; + if (distanceX != 0) { + int direction = (distanceX > 0) ? 1 : -1; + if (direction != mPreviousDirection) { + // The user has switched the direction of scrolling + // so re-init the next view + initNextView(-mViewStartX); + mPreviousDirection = direction; + } + } + } + + if ((mTouchMode & TOUCH_MODE_VSCROLL) != 0) { + // Calculate the top of the visible region in the calendar grid. + // Increasing/decrease this will scroll the calendar grid up/down. + mViewStartY = (int) ((mGestureCenterHour * (mCellHeight + DAY_GAP)) + - focusY + DAY_HEADER_HEIGHT + mAlldayHeight); + + // If dragging while already at the end, do a glow + final int pulledToY = (int) (mScrollStartY + deltaY); + if (pulledToY < 0) { + mEdgeEffectTop.onPull(deltaY / mViewHeight); + if (!mEdgeEffectBottom.isFinished()) { + mEdgeEffectBottom.onRelease(); + } + } else if (pulledToY > mMaxViewStartY) { + mEdgeEffectBottom.onPull(deltaY / mViewHeight); + if (!mEdgeEffectTop.isFinished()) { + mEdgeEffectTop.onRelease(); + } + } + + if (mViewStartY < 0) { + mViewStartY = 0; + mRecalCenterHour = true; + } else if (mViewStartY > mMaxViewStartY) { + mViewStartY = mMaxViewStartY; + mRecalCenterHour = true; + } + if (mRecalCenterHour) { + // Calculate the hour that correspond to the average of the Y touch points + mGestureCenterHour = (mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) + / (mCellHeight + DAY_GAP); + mRecalCenterHour = false; + } + computeFirstHour(); + } + + mScrolling = true; + + mSelectionMode = SELECTION_HIDDEN; + invalidate(); + } + + private float getAverageY(MotionEvent me) { + int count = me.getPointerCount(); + float focusY = 0; + for (int i = 0; i < count; i++) { + focusY += me.getY(i); + } + focusY /= count; + return focusY; + } + + private void cancelAnimation() { + Animation in = mViewSwitcher.getInAnimation(); + if (in != null) { + // cancel() doesn't terminate cleanly. + in.scaleCurrentDuration(0); + } + Animation out = mViewSwitcher.getOutAnimation(); + if (out != null) { + // cancel() doesn't terminate cleanly. + out.scaleCurrentDuration(0); + } + } + + private void doFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + cancelAnimation(); + + mSelectionMode = SELECTION_HIDDEN; + eventClickCleanup(); + + mOnFlingCalled = true; + + if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { + // Horizontal fling. + // initNextView(deltaX); + mTouchMode = TOUCH_MODE_INITIAL_STATE; + if (DEBUG) Log.d(TAG, "doFling: velocityX " + velocityX); + int deltaX = (int) e2.getX() - (int) e1.getX(); + switchViews(deltaX < 0, mViewStartX, mViewWidth, velocityX); + mViewStartX = 0; + return; + } + + if ((mTouchMode & TOUCH_MODE_VSCROLL) == 0) { + if (DEBUG) Log.d(TAG, "doFling: no fling"); + return; + } + + // Vertical fling. + mTouchMode = TOUCH_MODE_INITIAL_STATE; + mViewStartX = 0; + + if (DEBUG) { + Log.d(TAG, "doFling: mViewStartY" + mViewStartY + " velocityY " + velocityY); + } + + // Continue scrolling vertically + mScrolling = true; + mScroller.fling(0 /* startX */, mViewStartY /* startY */, 0 /* velocityX */, + (int) -velocityY, 0 /* minX */, 0 /* maxX */, 0 /* minY */, + mMaxViewStartY /* maxY */, OVERFLING_DISTANCE, OVERFLING_DISTANCE); + + // When flinging down, show a glow when it hits the end only if it + // wasn't started at the top + if (velocityY > 0 && mViewStartY != 0) { + mCallEdgeEffectOnAbsorb = true; + } + // When flinging up, show a glow when it hits the end only if it wasn't + // started at the bottom + else if (velocityY < 0 && mViewStartY != mMaxViewStartY) { + mCallEdgeEffectOnAbsorb = true; + } + mHandler.post(mContinueScroll); + } + + private boolean initNextView(int deltaX) { + // Change the view to the previous day or week + DayView view = (DayView) mViewSwitcher.getNextView(); + Time date = view.mBaseDate; + date.set(mBaseDate); + boolean switchForward; + if (deltaX > 0) { + date.monthDay -= mNumDays; + view.setSelectedDay(mSelectionDay - mNumDays); + switchForward = false; + } else { + date.monthDay += mNumDays; + view.setSelectedDay(mSelectionDay + mNumDays); + switchForward = true; + } + date.normalize(true /* ignore isDst */); + initView(view); + view.layout(getLeft(), getTop(), getRight(), getBottom()); + view.reloadEvents(); + return switchForward; + } + + // ScaleGestureDetector.OnScaleGestureListener + public boolean onScaleBegin(ScaleGestureDetector detector) { + mHandleActionUp = false; + float gestureCenterInPixels = detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight; + mGestureCenterHour = (mViewStartY + gestureCenterInPixels) / (mCellHeight + DAY_GAP); + + mStartingSpanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY())); + mCellHeightBeforeScaleGesture = mCellHeight; + + if (DEBUG_SCALING) { + float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP); + Log.d(TAG, "onScaleBegin: mGestureCenterHour:" + mGestureCenterHour + + "\tViewStartHour: " + ViewStartHour + "\tmViewStartY:" + mViewStartY + + "\tmCellHeight:" + mCellHeight + " SpanY:" + detector.getCurrentSpanY()); + } + + return true; + } + + // ScaleGestureDetector.OnScaleGestureListener + public boolean onScale(ScaleGestureDetector detector) { + float spanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY())); + + mCellHeight = (int) (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY); + + if (mCellHeight < mMinCellHeight) { + // If mStartingSpanY is too small, even a small increase in the + // gesture can bump the mCellHeight beyond MAX_CELL_HEIGHT + mStartingSpanY = spanY; + mCellHeight = mMinCellHeight; + mCellHeightBeforeScaleGesture = mMinCellHeight; + } else if (mCellHeight > MAX_CELL_HEIGHT) { + mStartingSpanY = spanY; + mCellHeight = MAX_CELL_HEIGHT; + mCellHeightBeforeScaleGesture = MAX_CELL_HEIGHT; + } + + int gestureCenterInPixels = (int) detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight; + mViewStartY = (int) (mGestureCenterHour * (mCellHeight + DAY_GAP)) - gestureCenterInPixels; + mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight; + + if (DEBUG_SCALING) { + float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP); + Log.d(TAG, "onScale: mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: " + + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:" + + mCellHeight + " SpanY:" + detector.getCurrentSpanY()); + } + + if (mViewStartY < 0) { + mViewStartY = 0; + mGestureCenterHour = (mViewStartY + gestureCenterInPixels) + / (float) (mCellHeight + DAY_GAP); + } else if (mViewStartY > mMaxViewStartY) { + mViewStartY = mMaxViewStartY; + mGestureCenterHour = (mViewStartY + gestureCenterInPixels) + / (float) (mCellHeight + DAY_GAP); + } + computeFirstHour(); + + mRemeasure = true; + invalidate(); + return true; + } + + // ScaleGestureDetector.OnScaleGestureListener + public void onScaleEnd(ScaleGestureDetector detector) { + mScrollStartY = mViewStartY; + mInitialScrollY = 0; + mInitialScrollX = 0; + mStartingSpanY = 0; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + int action = ev.getAction(); + if (DEBUG) Log.e(TAG, "" + action + " ev.getPointerCount() = " + ev.getPointerCount()); + + if ((ev.getActionMasked() == MotionEvent.ACTION_DOWN) || + (ev.getActionMasked() == MotionEvent.ACTION_UP) || + (ev.getActionMasked() == MotionEvent.ACTION_POINTER_UP) || + (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN)) { + mRecalCenterHour = true; + } + + if ((mTouchMode & TOUCH_MODE_HSCROLL) == 0) { + mScaleGestureDetector.onTouchEvent(ev); + } + + switch (action) { + case MotionEvent.ACTION_DOWN: + mStartingScroll = true; + if (DEBUG) { + Log.e(TAG, "ACTION_DOWN ev.getDownTime = " + ev.getDownTime() + " Cnt=" + + ev.getPointerCount()); + } + + int bottom = mAlldayHeight + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN; + if (ev.getY() < bottom) { + mTouchStartedInAlldayArea = true; + } else { + mTouchStartedInAlldayArea = false; + } + mHandleActionUp = true; + mGestureDetector.onTouchEvent(ev); + return true; + + case MotionEvent.ACTION_MOVE: + if (DEBUG) Log.e(TAG, "ACTION_MOVE Cnt=" + ev.getPointerCount() + DayView.this); + mGestureDetector.onTouchEvent(ev); + return true; + + case MotionEvent.ACTION_UP: + if (DEBUG) Log.e(TAG, "ACTION_UP Cnt=" + ev.getPointerCount() + mHandleActionUp); + mEdgeEffectTop.onRelease(); + mEdgeEffectBottom.onRelease(); + mStartingScroll = false; + mGestureDetector.onTouchEvent(ev); + if (!mHandleActionUp) { + mHandleActionUp = true; + mViewStartX = 0; + invalidate(); + return true; + } + + if (mOnFlingCalled) { + return true; + } + + // If we were scrolling, then reset the selected hour so that it + // is visible. + if (mScrolling) { + mScrolling = false; + resetSelectedHour(); + invalidate(); + } + + if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { + mTouchMode = TOUCH_MODE_INITIAL_STATE; + if (Math.abs(mViewStartX) > mHorizontalSnapBackThreshold) { + // The user has gone beyond the threshold so switch views + if (DEBUG) Log.d(TAG, "- horizontal scroll: switch views"); + switchViews(mViewStartX > 0, mViewStartX, mViewWidth, 0); + mViewStartX = 0; + return true; + } else { + // Not beyond the threshold so invalidate which will cause + // the view to snap back. Also call recalc() to ensure + // that we have the correct starting date and title. + if (DEBUG) Log.d(TAG, "- horizontal scroll: snap back"); + recalc(); + invalidate(); + mViewStartX = 0; + } + } + + return true; + + // This case isn't expected to happen. + case MotionEvent.ACTION_CANCEL: + if (DEBUG) Log.e(TAG, "ACTION_CANCEL"); + mGestureDetector.onTouchEvent(ev); + mScrolling = false; + resetSelectedHour(); + return true; + + default: + if (DEBUG) Log.e(TAG, "Not MotionEvent " + ev.toString()); + if (mGestureDetector.onTouchEvent(ev)) { + return true; + } + return super.onTouchEvent(ev); + } + } + + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) { + MenuItem item; + + // If the trackball is held down, then the context menu pops up and + // we never get onKeyUp() for the long-press. So check for it here + // and change the selection to the long-press state. + if (mSelectionMode != SELECTION_LONGPRESS) { + invalidate(); + } + + final long startMillis = getSelectedTimeInMillis(); + int flags = DateUtils.FORMAT_SHOW_TIME + | DateUtils.FORMAT_CAP_NOON_MIDNIGHT + | DateUtils.FORMAT_SHOW_WEEKDAY; + final String title = Utils.formatDateRange(mContext, startMillis, startMillis, flags); + menu.setHeaderTitle(title); + + mPopup.dismiss(); + } + + /** + * Sets mSelectionDay and mSelectionHour based on the (x,y) touch position. + * If the touch position is not within the displayed grid, then this + * method returns false. + * + * @param x the x position of the touch + * @param y the y position of the touch + * @param keepOldSelection - do not change the selection info (used for invoking accessibility + * messages) + * @return true if the touch position is valid + */ + private boolean setSelectionFromPosition(int x, final int y, boolean keepOldSelection) { + + Event savedEvent = null; + int savedDay = 0; + int savedHour = 0; + boolean savedAllDay = false; + if (keepOldSelection) { + // Store selection info and restore it at the end. This way, we can invoke the + // right accessibility message without affecting the selection. + savedEvent = mSelectedEvent; + savedDay = mSelectionDay; + savedHour = mSelectionHour; + savedAllDay = mSelectionAllday; + } + if (x < mHoursWidth) { + x = mHoursWidth; + } + + int day = (x - mHoursWidth) / (mCellWidth + DAY_GAP); + if (day >= mNumDays) { + day = mNumDays - 1; + } + day += mFirstJulianDay; + setSelectedDay(day); + + if (y < DAY_HEADER_HEIGHT) { + sendAccessibilityEventAsNeeded(false); + return false; + } + + setSelectedHour(mFirstHour); /* First fully visible hour */ + + if (y < mFirstCell) { + mSelectionAllday = true; + } else { + // y is now offset from top of the scrollable region + int adjustedY = y - mFirstCell; + + if (adjustedY < mFirstHourOffset) { + setSelectedHour(mSelectionHour - 1); /* In the partially visible hour */ + } else { + setSelectedHour(mSelectionHour + + (adjustedY - mFirstHourOffset) / (mCellHeight + HOUR_GAP)); + } + + mSelectionAllday = false; + } + + findSelectedEvent(x, y); + + sendAccessibilityEventAsNeeded(true); + + // Restore old values + if (keepOldSelection) { + mSelectedEvent = savedEvent; + mSelectionDay = savedDay; + mSelectionHour = savedHour; + mSelectionAllday = savedAllDay; + } + return true; + } + + private void findSelectedEvent(int x, int y) { + int date = mSelectionDay; + int cellWidth = mCellWidth; + ArrayList events = mEvents; + int numEvents = events.size(); + int left = computeDayLeftPosition(mSelectionDay - mFirstJulianDay); + int top = 0; + setSelectedEvent(null); + + mSelectedEvents.clear(); + if (mSelectionAllday) { + float yDistance; + float minYdistance = 10000.0f; // any large number + Event closestEvent = null; + float drawHeight = mAlldayHeight; + int yOffset = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN; + int maxUnexpandedColumn = mMaxUnexpandedAlldayEventCount; + if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { + // Leave a gap for the 'box +n' text + maxUnexpandedColumn--; + } + events = mAllDayEvents; + numEvents = events.size(); + for (int i = 0; i < numEvents; i++) { + Event event = events.get(i); + if (!event.drawAsAllday() || + (!mShowAllAllDayEvents && event.getColumn() >= maxUnexpandedColumn)) { + // Don't check non-allday events or events that aren't shown + continue; + } + + if (event.startDay <= mSelectionDay && event.endDay >= mSelectionDay) { + float numRectangles = mShowAllAllDayEvents ? mMaxAlldayEvents + : mMaxUnexpandedAlldayEventCount; + float height = drawHeight / numRectangles; + if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) { + height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; + } + float eventTop = yOffset + height * event.getColumn(); + float eventBottom = eventTop + height; + if (eventTop < y && eventBottom > y) { + // If the touch is inside the event rectangle, then + // add the event. + mSelectedEvents.add(event); + closestEvent = event; + break; + } else { + // Find the closest event + if (eventTop >= y) { + yDistance = eventTop - y; + } else { + yDistance = y - eventBottom; + } + if (yDistance < minYdistance) { + minYdistance = yDistance; + closestEvent = event; + } + } + } + } + setSelectedEvent(closestEvent); + return; + } + + // Adjust y for the scrollable bitmap + y += mViewStartY - mFirstCell; + + // Use a region around (x,y) for the selection region + Rect region = mRect; + region.left = x - 10; + region.right = x + 10; + region.top = y - 10; + region.bottom = y + 10; + + EventGeometry geometry = mEventGeometry; + + for (int i = 0; i < numEvents; i++) { + Event event = events.get(i); + // Compute the event rectangle. + if (!geometry.computeEventRect(date, left, top, cellWidth, event)) { + continue; + } + + // If the event intersects the selection region, then add it to + // mSelectedEvents. + if (geometry.eventIntersectsSelection(event, region)) { + mSelectedEvents.add(event); + } + } + + // If there are any events in the selected region, then assign the + // closest one to mSelectedEvent. + if (mSelectedEvents.size() > 0) { + int len = mSelectedEvents.size(); + Event closestEvent = null; + float minDist = mViewWidth + mViewHeight; // some large distance + for (int index = 0; index < len; index++) { + Event ev = mSelectedEvents.get(index); + float dist = geometry.pointToEvent(x, y, ev); + if (dist < minDist) { + minDist = dist; + closestEvent = ev; + } + } + setSelectedEvent(closestEvent); + + // Keep the selected hour and day consistent with the selected + // event. They could be different if we touched on an empty hour + // slot very close to an event in the previous hour slot. In + // that case we will select the nearby event. + int startDay = mSelectedEvent.startDay; + int endDay = mSelectedEvent.endDay; + if (mSelectionDay < startDay) { + setSelectedDay(startDay); + } else if (mSelectionDay > endDay) { + setSelectedDay(endDay); + } + + int startHour = mSelectedEvent.startTime / 60; + int endHour; + if (mSelectedEvent.startTime < mSelectedEvent.endTime) { + endHour = (mSelectedEvent.endTime - 1) / 60; + } else { + endHour = mSelectedEvent.endTime / 60; + } + + if (mSelectionHour < startHour && mSelectionDay == startDay) { + setSelectedHour(startHour); + } else if (mSelectionHour > endHour && mSelectionDay == endDay) { + setSelectedHour(endHour); + } + } + } + + // Encapsulates the code to continue the scrolling after the + // finger is lifted. Instead of stopping the scroll immediately, + // the scroll continues to "free spin" and gradually slows down. + private class ContinueScroll implements Runnable { + + public void run() { + mScrolling = mScrolling && mScroller.computeScrollOffset(); + if (!mScrolling || mPaused) { + resetSelectedHour(); + invalidate(); + return; + } + + mViewStartY = mScroller.getCurrY(); + + if (mCallEdgeEffectOnAbsorb) { + if (mViewStartY < 0) { + mEdgeEffectTop.onAbsorb((int) mLastVelocity); + mCallEdgeEffectOnAbsorb = false; + } else if (mViewStartY > mMaxViewStartY) { + mEdgeEffectBottom.onAbsorb((int) mLastVelocity); + mCallEdgeEffectOnAbsorb = false; + } + mLastVelocity = mScroller.getCurrVelocity(); + } + + if (mScrollStartY == 0 || mScrollStartY == mMaxViewStartY) { + // Allow overscroll/springback only on a fling, + // not a pull/fling from the end + if (mViewStartY < 0) { + mViewStartY = 0; + } else if (mViewStartY > mMaxViewStartY) { + mViewStartY = mMaxViewStartY; + } + } + + computeFirstHour(); + mHandler.post(this); + invalidate(); + } + } + + /** + * Cleanup the pop-up and timers. + */ + public void cleanup() { + // Protect against null-pointer exceptions + if (mPopup != null) { + mPopup.dismiss(); + } + mPaused = true; + mLastPopupEventID = INVALID_EVENT_ID; + if (mHandler != null) { + mHandler.removeCallbacks(mDismissPopup); + mHandler.removeCallbacks(mUpdateCurrentTime); + } + + Utils.setSharedPreference(mContext, GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, + mCellHeight); + // Clear all click animations + eventClickCleanup(); + // Turn off redraw + mRemeasure = false; + // Turn off scrolling to make sure the view is in the correct state if we fling back to it + mScrolling = false; + } + + private void eventClickCleanup() { + this.removeCallbacks(mClearClick); + this.removeCallbacks(mSetClick); + mClickedEvent = null; + mSavedClickedEvent = null; + } + + private void setSelectedEvent(Event e) { + mSelectedEvent = e; + mSelectedEventForAccessibility = e; + } + + private void setSelectedHour(int h) { + mSelectionHour = h; + mSelectionHourForAccessibility = h; + } + private void setSelectedDay(int d) { + mSelectionDay = d; + mSelectionDayForAccessibility = d; + } + + /** + * Restart the update timer + */ + public void restartCurrentTimeUpdates() { + mPaused = false; + if (mHandler != null) { + mHandler.removeCallbacks(mUpdateCurrentTime); + mHandler.post(mUpdateCurrentTime); + } + } + + @Override + protected void onDetachedFromWindow() { + cleanup(); + super.onDetachedFromWindow(); + } + + class DismissPopup implements Runnable { + + public void run() { + // Protect against null-pointer exceptions + if (mPopup != null) { + mPopup.dismiss(); + } + } + } + + class UpdateCurrentTime implements Runnable { + + public void run() { + long currentTime = System.currentTimeMillis(); + mCurrentTime.set(currentTime); + //% causes update to occur on 5 minute marks (11:10, 11:15, 11:20, etc.) + if (!DayView.this.mPaused) { + mHandler.postDelayed(mUpdateCurrentTime, UPDATE_CURRENT_TIME_DELAY + - (currentTime % UPDATE_CURRENT_TIME_DELAY)); + } + mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff); + invalidate(); + } + } + + class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { + @Override + public boolean onSingleTapUp(MotionEvent ev) { + if (DEBUG) Log.e(TAG, "GestureDetector.onSingleTapUp"); + DayView.this.doSingleTapUp(ev); + return true; + } + + @Override + public void onLongPress(MotionEvent ev) { + if (DEBUG) Log.e(TAG, "GestureDetector.onLongPress"); + DayView.this.doLongPress(ev); + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + if (DEBUG) Log.e(TAG, "GestureDetector.onScroll"); + eventClickCleanup(); + if (mTouchStartedInAlldayArea) { + if (Math.abs(distanceX) < Math.abs(distanceY)) { + // Make sure that click feedback is gone when you scroll from the + // all day area + invalidate(); + return false; + } + // don't scroll vertically if this started in the allday area + distanceY = 0; + } + DayView.this.doScroll(e1, e2, distanceX, distanceY); + return true; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (DEBUG) Log.e(TAG, "GestureDetector.onFling"); + + if (mTouchStartedInAlldayArea) { + if (Math.abs(velocityX) < Math.abs(velocityY)) { + return false; + } + // don't fling vertically if this started in the allday area + velocityY = 0; + } + DayView.this.doFling(e1, e2, velocityX, velocityY); + return true; + } + + @Override + public boolean onDown(MotionEvent ev) { + if (DEBUG) Log.e(TAG, "GestureDetector.onDown"); + DayView.this.doDown(ev); + return true; + } + } + + @Override + public boolean onLongClick(View v) { + return true; + } + + // The rest of this file was borrowed from Launcher2 - PagedView.java + private static final int MINIMUM_SNAP_VELOCITY = 2200; + + private class ScrollInterpolator implements Interpolator { + public ScrollInterpolator() { + } + + public float getInterpolation(float t) { + t -= 1.0f; + t = t * t * t * t * t + 1; + + if ((1 - t) * mAnimationDistance < 1) { + cancelAnimation(); + } + + return t; + } + } + + private long calculateDuration(float delta, float width, float velocity) { + /* + * Here we compute a "distance" that will be used in the computation of + * the overall snap duration. This is a function of the actual distance + * that needs to be traveled; we keep this value close to half screen + * size in order to reduce the variance in snap duration as a function + * of the distance the page needs to travel. + */ + final float halfScreenSize = width / 2; + float distanceRatio = delta / width; + float distanceInfluenceForSnapDuration = distanceInfluenceForSnapDuration(distanceRatio); + float distance = halfScreenSize + halfScreenSize * distanceInfluenceForSnapDuration; + + velocity = Math.abs(velocity); + velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity); + + /* + * we want the page's snap velocity to approximately match the velocity + * at which the user flings, so we scale the duration by a value near to + * the derivative of the scroll interpolator at zero, ie. 5. We use 6 to + * make it a little slower. + */ + long duration = 6 * Math.round(1000 * Math.abs(distance / velocity)); + if (DEBUG) { + Log.e(TAG, "halfScreenSize:" + halfScreenSize + " delta:" + delta + " distanceRatio:" + + distanceRatio + " distance:" + distance + " velocity:" + velocity + + " duration:" + duration + " distanceInfluenceForSnapDuration:" + + distanceInfluenceForSnapDuration); + } + return duration; + } + + /* + * We want the duration of the page snap animation to be influenced by the + * distance that the screen has to travel, however, we don't want this + * duration to be effected in a purely linear fashion. Instead, we use this + * method to moderate the effect that the distance of travel has on the + * overall snap duration. + */ + private float distanceInfluenceForSnapDuration(float f) { + f -= 0.5f; // center the values about 0. + f *= 0.3f * Math.PI / 2.0f; + return (float) Math.sin(f); + } +} -- cgit v1.2.3 From 9b97629d83b230d421c2c8591eaad0e279851bc7 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 1 Jul 2021 17:39:42 +0000 Subject: AOSP/Calendar - Initial Conversion of DayView This is how Android Studio's built-in converter converted DayView.java to Kotlin without any additional edits. Change-Id: Icc9a779706d9962b2edd4975d1df2b209cb9dea0 --- src/com/android/calendar/DayView.kt | 5140 +++++++++++++++++------------------ 1 file changed, 2514 insertions(+), 2626 deletions(-) diff --git a/src/com/android/calendar/DayView.kt b/src/com/android/calendar/DayView.kt index 2fc00b3c..ce8ad124 100644 --- a/src/com/android/calendar/DayView.kt +++ b/src/com/android/calendar/DayView.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,119 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar -package com.android.calendar; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.app.AlertDialog; -import android.app.Service; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.Context; -import android.content.DialogInterface; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.database.Cursor; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Paint.Style; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Handler; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.text.Layout.Alignment; -import android.text.SpannableStringBuilder; -import android.text.StaticLayout; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.text.style.StyleSpan; -import android.util.Log; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.GestureDetector; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.Animation; -import android.view.animation.Interpolator; -import android.view.animation.TranslateAnimation; -import android.widget.EdgeEffect; -import android.widget.ImageView; -import android.widget.OverScroller; -import android.widget.PopupWindow; -import android.widget.TextView; -import android.widget.ViewSwitcher; - -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.CalendarController.ViewType; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Formatter; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import android.animation.Animator /** * View for multi-day view. So far only 1 and 7 day have been tested. */ -public class DayView extends View implements View.OnCreateContextMenuListener, - ScaleGestureDetector.OnScaleGestureListener, View.OnClickListener, View.OnLongClickListener - { - private static String TAG = "DayView"; - private static boolean DEBUG = false; - private static boolean DEBUG_SCALING = false; - private static final String PERIOD_SPACE = ". "; - - private static float mScale = 0; // Used for supporting different screen densities - private static final long INVALID_EVENT_ID = -1; //This is used for remembering a null event - // Duration of the allday expansion - private static final long ANIMATION_DURATION = 400; - // duration of the more allday event text fade - private static final long ANIMATION_SECONDARY_DURATION = 200; - // duration of the scroll to go to a specified time - private static final int GOTO_SCROLL_DURATION = 200; - // duration for events' cross-fade animation - private static final int EVENTS_CROSS_FADE_DURATION = 400; - // duration to show the event clicked - private static final int CLICK_DISPLAY_DURATION = 50; - - private static final int MENU_DAY = 3; - private static final int MENU_EVENT_VIEW = 5; - private static final int MENU_EVENT_CREATE = 6; - private static final int MENU_EVENT_EDIT = 7; - private static final int MENU_EVENT_DELETE = 8; - - private static int DEFAULT_CELL_HEIGHT = 64; - private static int MAX_CELL_HEIGHT = 150; - private static int MIN_Y_SPAN = 100; - - private boolean mOnFlingCalled; - private boolean mStartingScroll = false; - protected boolean mPaused = true; - private Handler mHandler; +class DayView( + context: Context, controller: CalendarController, + viewSwitcher: ViewSwitcher, eventLoader: EventLoader, numDays: Int +) : View(context), View.OnCreateContextMenuListener, ScaleGestureDetector.OnScaleGestureListener, + View.OnClickListener, View.OnLongClickListener { + private var mOnFlingCalled = false + private var mStartingScroll = false + protected var mPaused = true + private var mHandler: Handler? = null + /** * ID of the last event which was displayed with the toast popup. * @@ -134,166 +38,137 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * on starting calendar or by scrolling to an event. It becomes invalid when the user * explicitly scrolls to an empty time slot, changes views, or deletes the event. */ - private long mLastPopupEventID; - - protected Context mContext; - - private static final String[] CALENDARS_PROJECTION = new String[] { - Calendars._ID, // 0 - Calendars.CALENDAR_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 int FROM_NONE = 0; - private static final int FROM_ABOVE = 1; - private static final int FROM_BELOW = 2; - private static final int FROM_LEFT = 4; - private static final int FROM_RIGHT = 8; - - private static final int ACCESS_LEVEL_NONE = 0; - private static final int ACCESS_LEVEL_DELETE = 1; - private static final int ACCESS_LEVEL_EDIT = 2; - - private static int mHorizontalSnapBackThreshold = 128; - - private final ContinueScroll mContinueScroll = new ContinueScroll(); + private var mLastPopupEventID: Long + protected var mContext: Context + private val mContinueScroll: ContinueScroll = ContinueScroll() // Make this visible within the package for more informative debugging - Time mBaseDate; - private Time mCurrentTime; - //Update the current time line every five minutes if the window is left open that long - private static final int UPDATE_CURRENT_TIME_DELAY = 300000; - private final UpdateCurrentTime mUpdateCurrentTime = new UpdateCurrentTime(); - private int mTodayJulianDay; - - private final Typeface mBold = Typeface.DEFAULT_BOLD; - private int mFirstJulianDay; - private int mLoadedFirstJulianDay = -1; - private int mLastJulianDay; - - private int mMonthLength; - private int mFirstVisibleDate; - private int mFirstVisibleDayOfWeek; - private int[] mEarliestStartHour; // indexed by the week day offset - private boolean[] mHasAllDayEvent; // indexed by the week day offset - private String mEventCountTemplate; - private Event mClickedEvent; // The event the user clicked on - private Event mSavedClickedEvent; - private static int mOnDownDelay; - private int mClickedYLocation; - private long mDownTouchTime; - - private int mEventsAlpha = 255; - private ObjectAnimator mEventsCrossFadeAnimation; - - protected static StringBuilder mStringBuilder = new StringBuilder(50); - // TODO recreate formatter when locale changes - protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); - - private final Runnable mTZUpdater = new Runnable() { + var mBaseDate: Time? = null + private var mCurrentTime: Time? = null + private val mUpdateCurrentTime: UpdateCurrentTime = UpdateCurrentTime() + private var mTodayJulianDay = 0 + private val mBold: Typeface = Typeface.DEFAULT_BOLD + private var mFirstJulianDay = 0 + private var mLoadedFirstJulianDay = -1 + private var mLastJulianDay = 0 + private var mMonthLength = 0 + private var mFirstVisibleDate = 0 + private var mFirstVisibleDayOfWeek = 0 + private var mEarliestStartHour // indexed by the week day offset + : IntArray + private var mHasAllDayEvent // indexed by the week day offset + : BooleanArray + private var mEventCountTemplate: String? = null + private var mClickedEvent // The event the user clicked on + : Event? = null + private var mSavedClickedEvent: Event? = null + private var mClickedYLocation = 0 + private var mDownTouchTime: Long = 0 + private var mEventsAlpha = 255 + private var mEventsCrossFadeAnimation: ObjectAnimator? = null + private val mTZUpdater: Runnable = object : Runnable() { @Override - public void run() { - String tz = Utils.getTimeZone(mContext, this); - mBaseDate.timezone = tz; - mBaseDate.normalize(true); - mCurrentTime.switchTimezone(tz); - invalidate(); + fun run() { + val tz: String = Utils.getTimeZone(mContext, this) + mBaseDate.timezone = tz + mBaseDate.normalize(true) + mCurrentTime.switchTimezone(tz) + invalidate() } - }; + } // Sets the "clicked" color from the clicked event - private final Runnable mSetClick = new Runnable() { + private val mSetClick: Runnable = object : Runnable() { @Override - public void run() { - mClickedEvent = mSavedClickedEvent; - mSavedClickedEvent = null; - DayView.this.invalidate(); + fun run() { + mClickedEvent = mSavedClickedEvent + mSavedClickedEvent = null + this@DayView.invalidate() } - }; + } // Clears the "clicked" color from the clicked event and launch the event - private final Runnable mClearClick = new Runnable() { + private val mClearClick: Runnable = object : Runnable() { @Override - public void run() { + fun run() { if (mClickedEvent != null) { - mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, mClickedEvent.id, - mClickedEvent.startMillis, mClickedEvent.endMillis, - DayView.this.getWidth() / 2, mClickedYLocation, - getSelectedTimeInMillis()); + mController.sendEventRelatedEvent( + this, EventType.VIEW_EVENT, mClickedEvent.id, + mClickedEvent.startMillis, mClickedEvent.endMillis, + this@DayView.getWidth() / 2, mClickedYLocation, + selectedTimeInMillis + ) } - mClickedEvent = null; - DayView.this.invalidate(); + mClickedEvent = null + this@DayView.invalidate() } - }; - - private final TodayAnimatorListener mTodayAnimatorListener = new TodayAnimatorListener(); + } + private val mTodayAnimatorListener: TodayAnimatorListener = TodayAnimatorListener() - class TodayAnimatorListener extends AnimatorListenerAdapter { - private volatile Animator mAnimator = null; - private volatile boolean mFadingIn = false; + internal inner class TodayAnimatorListener : AnimatorListenerAdapter() { + @Volatile + private var mAnimator: Animator? = null + @Volatile + private var mFadingIn = false @Override - public void onAnimationEnd(Animator animation) { - synchronized (this) { - if (mAnimator != animation) { - animation.removeAllListeners(); - animation.cancel(); - return; + fun onAnimationEnd(animation: Animator) { + synchronized(this) { + if (mAnimator !== animation) { + animation.removeAllListeners() + animation.cancel() + return } if (mFadingIn) { if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners(); - mTodayAnimator.cancel(); + mTodayAnimator.removeAllListeners() + mTodayAnimator.cancel() } mTodayAnimator = ObjectAnimator - .ofInt(DayView.this, "animateTodayAlpha", 255, 0); - mAnimator = mTodayAnimator; - mFadingIn = false; - mTodayAnimator.addListener(this); - mTodayAnimator.setDuration(600); - mTodayAnimator.start(); + .ofInt(this@DayView, "animateTodayAlpha", 255, 0) + mAnimator = mTodayAnimator + mFadingIn = false + mTodayAnimator.addListener(this) + mTodayAnimator.setDuration(600) + mTodayAnimator.start() } else { - mAnimateToday = false; - mAnimateTodayAlpha = 0; - mAnimator.removeAllListeners(); - mAnimator = null; - mTodayAnimator = null; - invalidate(); + mAnimateToday = false + mAnimateTodayAlpha = 0 + mAnimator.removeAllListeners() + mAnimator = null + mTodayAnimator = null + invalidate() } } } - public void setAnimator(Animator animation) { - mAnimator = animation; + fun setAnimator(animation: Animator?) { + mAnimator = animation } - public void setFadingIn(boolean fadingIn) { - mFadingIn = fadingIn; + fun setFadingIn(fadingIn: Boolean) { + mFadingIn = fadingIn } - } - AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() { + var mAnimatorListener: AnimatorListenerAdapter = object : AnimatorListenerAdapter() { @Override - public void onAnimationStart(Animator animation) { - mScrolling = true; + fun onAnimationStart(animation: Animator?) { + mScrolling = true } @Override - public void onAnimationCancel(Animator animation) { - mScrolling = false; + fun onAnimationCancel(animation: Animator?) { + mScrolling = false } @Override - public void onAnimationEnd(Animator animation) { - mScrolling = false; - resetSelectedHour(); - invalidate(); + fun onAnimationEnd(animation: Animator?) { + mScrolling = false + resetSelectedHour() + invalidate() } - }; + } /** * This variable helps to avoid unnecessarily reloading events by keeping @@ -302,824 +177,550 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * reloaded. To force a reload, set this to zero (this is set to zero * in the method clearCachedEvents()). */ - private long mLastReloadMillis; - - private ArrayList mEvents = new ArrayList(); - private ArrayList mAllDayEvents = new ArrayList(); - private StaticLayout[] mLayouts = null; - private StaticLayout[] mAllDayLayouts = null; - private int mSelectionDay; // Julian day - private int mSelectionHour; - - boolean mSelectionAllday; + private var mLastReloadMillis: Long = 0 + private var mEvents: ArrayList = ArrayList() + private var mAllDayEvents: ArrayList? = ArrayList() + private var mLayouts: Array? = null + private var mAllDayLayouts: Array? = null + private var mSelectionDay // Julian day + = 0 + private var mSelectionHour = 0 + var mSelectionAllday = false // Current selection info for accessibility - private int mSelectionDayForAccessibility; // Julian day - private int mSelectionHourForAccessibility; - private Event mSelectedEventForAccessibility; - // Last selection info for accessibility - private int mLastSelectionDayForAccessibility; - private int mLastSelectionHourForAccessibility; - private Event mLastSelectedEventForAccessibility; + private var mSelectionDayForAccessibility // Julian day + = 0 + private var mSelectionHourForAccessibility = 0 + private var mSelectedEventForAccessibility: Event? = null + // Last selection info for accessibility + private var mLastSelectionDayForAccessibility = 0 + private var mLastSelectionHourForAccessibility = 0 + private var mLastSelectedEventForAccessibility: Event? = null - /** Width of a day or non-conflicting event */ - private int mCellWidth; + /** Width of a day or non-conflicting event */ + private var mCellWidth = 0 // Pre-allocate these objects and re-use them - private final Rect mRect = new Rect(); - private final Rect mDestRect = new Rect(); - private final Rect mSelectionRect = new Rect(); - // This encloses the more allDay events icon - private final Rect mExpandAllDayRect = new Rect(); - // TODO Clean up paint usage - private final Paint mPaint = new Paint(); - private final Paint mEventTextPaint = new Paint(); - private final Paint mSelectionPaint = new Paint(); - private float[] mLines; - - private int mFirstDayOfWeek; // First day of the week - - private PopupWindow mPopup; - private View mPopupView; - - // The number of milliseconds to show the popup window - private static final int POPUP_DISMISS_DELAY = 3000; - private final DismissPopup mDismissPopup = new DismissPopup(); + private val mRect: Rect = Rect() + private val mDestRect: Rect = Rect() + private val mSelectionRect: Rect = Rect() - private boolean mRemeasure = true; - - private final EventLoader mEventLoader; - protected final EventGeometry mEventGeometry; - - private static float GRID_LINE_LEFT_MARGIN = 0; - private static final float GRID_LINE_INNER_WIDTH = 1; + // This encloses the more allDay events icon + private val mExpandAllDayRect: Rect = Rect() - private static final int DAY_GAP = 1; - private static final int HOUR_GAP = 1; - // This is the standard height of an allday event with no restrictions - private static int SINGLE_ALLDAY_HEIGHT = 34; - /** - * This is the minimum desired height of a allday event. - * When unexpanded, allday events will use this height. - * When expanded allDay events will attempt to grow to fit all - * events at this height. - */ - private static float MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = 28.0F; // in pixels - /** - * This is how big the unexpanded allday height is allowed to be. - * It will get adjusted based on screen size - */ - private static int MAX_UNEXPANDED_ALLDAY_HEIGHT = - (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4); - /** - * This is the minimum size reserved for displaying regular events. - * The expanded allDay region can't expand into this. - */ - private static int MIN_HOURS_HEIGHT = 180; - private static int ALLDAY_TOP_MARGIN = 1; - // The largest a single allDay event will become. - private static int MAX_HEIGHT_OF_ONE_ALLDAY_EVENT = 34; - - private static int HOURS_TOP_MARGIN = 2; - private static int HOURS_LEFT_MARGIN = 2; - private static int HOURS_RIGHT_MARGIN = 4; - private static int HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN; - private static int NEW_EVENT_MARGIN = 4; - private static int NEW_EVENT_WIDTH = 2; - private static int NEW_EVENT_MAX_LENGTH = 16; - - private static int CURRENT_TIME_LINE_SIDE_BUFFER = 4; - private static int CURRENT_TIME_LINE_TOP_OFFSET = 2; - - /* package */ static final int MINUTES_PER_HOUR = 60; - /* package */ static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * 24; - /* package */ static final int MILLIS_PER_MINUTE = 60 * 1000; - /* package */ static final int MILLIS_PER_HOUR = (3600 * 1000); - /* package */ static final int MILLIS_PER_DAY = MILLIS_PER_HOUR * 24; - - // More events text will transition between invisible and this alpha - private static final int MORE_EVENTS_MAX_ALPHA = 0x4C; - private static int DAY_HEADER_ONE_DAY_LEFT_MARGIN = 0; - private static int DAY_HEADER_ONE_DAY_RIGHT_MARGIN = 5; - private static int DAY_HEADER_ONE_DAY_BOTTOM_MARGIN = 6; - private static int DAY_HEADER_RIGHT_MARGIN = 4; - private static int DAY_HEADER_BOTTOM_MARGIN = 3; - private static float DAY_HEADER_FONT_SIZE = 14; - private static float DATE_HEADER_FONT_SIZE = 32; - private static float NORMAL_FONT_SIZE = 12; - private static float EVENT_TEXT_FONT_SIZE = 12; - private static float HOURS_TEXT_SIZE = 12; - private static float AMPM_TEXT_SIZE = 9; - private static int MIN_HOURS_WIDTH = 96; - private static int MIN_CELL_WIDTH_FOR_TEXT = 20; - private static final int MAX_EVENT_TEXT_LEN = 500; - // smallest height to draw an event with - private static float MIN_EVENT_HEIGHT = 24.0F; // in pixels - private static int CALENDAR_COLOR_SQUARE_SIZE = 10; - private static int EVENT_RECT_TOP_MARGIN = 1; - private static int EVENT_RECT_BOTTOM_MARGIN = 0; - private static int EVENT_RECT_LEFT_MARGIN = 1; - private static int EVENT_RECT_RIGHT_MARGIN = 0; - private static int EVENT_RECT_STROKE_WIDTH = 2; - private static int EVENT_TEXT_TOP_MARGIN = 2; - private static int EVENT_TEXT_BOTTOM_MARGIN = 2; - private static int EVENT_TEXT_LEFT_MARGIN = 6; - private static int EVENT_TEXT_RIGHT_MARGIN = 6; - private static int ALL_DAY_EVENT_RECT_BOTTOM_MARGIN = 1; - private static int EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN; - private static int EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_BOTTOM_MARGIN; - private static int EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN; - private static int EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_RIGHT_MARGIN; - // margins and sizing for the expand allday icon - private static int EXPAND_ALL_DAY_BOTTOM_MARGIN = 10; - // sizing for "box +n" in allDay events - private static int EVENT_SQUARE_WIDTH = 10; - private static int EVENT_LINE_PADDING = 4; - private static int NEW_EVENT_HINT_FONT_SIZE = 12; - - private static int mEventTextColor; - private static int mMoreEventsTextColor; - - private static int mWeek_saturdayColor; - private static int mWeek_sundayColor; - private static int mCalendarDateBannerTextColor; - private static int mCalendarAmPmLabel; - private static int mCalendarGridAreaSelected; - private static int mCalendarGridLineInnerHorizontalColor; - private static int mCalendarGridLineInnerVerticalColor; - private static int mFutureBgColor; - private static int mFutureBgColorRes; - private static int mBgColor; - private static int mNewEventHintColor; - private static int mCalendarHourLabelColor; - private static int mMoreAlldayEventsTextAlpha = MORE_EVENTS_MAX_ALPHA; - - private float mAnimationDistance = 0; - private int mViewStartX; - private int mViewStartY; - private int mMaxViewStartY; - private int mViewHeight; - private int mViewWidth; - private int mGridAreaHeight = -1; - private static int mCellHeight = 0; // shared among all DayViews - private static int mMinCellHeight = 32; - private int mScrollStartY; - private int mPreviousDirection; - private static int mScaledPagingTouchSlop = 0; + // TODO Clean up paint usage + private val mPaint: Paint = Paint() + private val mEventTextPaint: Paint = Paint() + private val mSelectionPaint: Paint = Paint() + private var mLines: FloatArray + private var mFirstDayOfWeek // First day of the week + = 0 + private var mPopup: PopupWindow? = null + private var mPopupView: View? = null + private val mDismissPopup: DismissPopup = DismissPopup() + private var mRemeasure = true + private val mEventLoader: EventLoader + protected val mEventGeometry: EventGeometry + private var mAnimationDistance = 0f + private var mViewStartX = 0 + private var mViewStartY = 0 + private var mMaxViewStartY = 0 + private var mViewHeight = 0 + private var mViewWidth = 0 + private var mGridAreaHeight = -1 + private var mScrollStartY = 0 + private var mPreviousDirection = 0 /** * Vertical distance or span between the two touch points at the start of a * scaling gesture */ - private float mStartingSpanY = 0; - /** Height of 1 hour in pixels at the start of a scaling gesture */ - private int mCellHeightBeforeScaleGesture; - /** The hour at the center two touch points */ - private float mGestureCenterHour = 0; + private var mStartingSpanY = 0f - private boolean mRecalCenterHour = false; + /** Height of 1 hour in pixels at the start of a scaling gesture */ + private var mCellHeightBeforeScaleGesture = 0 + + /** The hour at the center two touch points */ + private var mGestureCenterHour = 0f + private var mRecalCenterHour = false /** * Flag to decide whether to handle the up event. Cases where up events * should be ignored are 1) right after a scale gesture and 2) finger was * down before app launch */ - private boolean mHandleActionUp = true; + private var mHandleActionUp = true + private var mHoursTextHeight = 0 - private int mHoursTextHeight; /** * The height of the area used for allday events */ - private int mAlldayHeight; - /** - * The height of the allday event area used during animation - */ - private int mAnimateDayHeight = 0; - /** - * The height of an individual allday event during animation - */ - private int mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; - /** - * Whether to use the expand or collapse icon. - */ - private static boolean mUseExpandIcon = true; - /** - * The height of the day names/numbers - */ - private static int DAY_HEADER_HEIGHT = 45; - /** - * The height of the day names/numbers for multi-day views - */ - private static int MULTI_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT; - /** - * The height of the day names/numbers when viewing a single day - */ - private static int ONE_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT; - /** - * Max of all day events in a given day in this view. - */ - private int mMaxAlldayEvents; - /** - * A count of the number of allday events that were not drawn for each day - */ - private int[] mSkippedAlldayEvents; - /** - * The number of allDay events at which point we start hiding allDay events. - */ - private int mMaxUnexpandedAlldayEventCount = 4; - /** - * Whether or not to expand the allDay area to fill the screen - */ - private static boolean mShowAllAllDayEvents = false; - - protected int mNumDays = 7; - private int mNumHours = 10; - - /** Width of the time line (list of hours) to the left. */ - private int mHoursWidth; - private int mDateStrWidth; - /** Top of the scrollable region i.e. below date labels and all day events */ - private int mFirstCell; - /** First fully visibile hour */ - private int mFirstHour = -1; - /** Distance between the mFirstCell and the top of first fully visible hour. */ - private int mFirstHourOffset; - private String[] mHourStrs; - private String[] mDayStrs; - private String[] mDayStrs2Letter; - private boolean mIs24HourFormat; - - private final ArrayList mSelectedEvents = new ArrayList(); - private boolean mComputeSelectedEvents; - private boolean mUpdateToast; - private Event mSelectedEvent; - private Event mPrevSelectedEvent; - private final Rect mPrevBox = new Rect(); - protected final Resources mResources; - protected final Drawable mCurrentTimeLine; - protected final Drawable mCurrentTimeAnimateLine; - protected final Drawable mTodayHeaderDrawable; - protected final Drawable mExpandAlldayDrawable; - protected final Drawable mCollapseAlldayDrawable; - protected Drawable mAcceptedOrTentativeEventBoxDrawable; - private String mAmString; - private String mPmString; - private static int sCounter = 0; - - ScaleGestureDetector mScaleGestureDetector; + private var mAlldayHeight = 0 /** - * The initial state of the touch mode when we enter this view. + * The height of the allday event area used during animation */ - private static final int TOUCH_MODE_INITIAL_STATE = 0; + private var mAnimateDayHeight = 0 /** - * Indicates we just received the touch event and we are waiting to see if - * it is a tap or a scroll gesture. + * The height of an individual allday event during animation */ - private static final int TOUCH_MODE_DOWN = 1; + private var mAnimateDayEventHeight = MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt() /** - * Indicates the touch gesture is a vertical scroll + * Max of all day events in a given day in this view. */ - private static final int TOUCH_MODE_VSCROLL = 0x20; + private var mMaxAlldayEvents = 0 /** - * Indicates the touch gesture is a horizontal scroll + * A count of the number of allday events that were not drawn for each day */ - private static final int TOUCH_MODE_HSCROLL = 0x40; - - private int mTouchMode = TOUCH_MODE_INITIAL_STATE; + private var mSkippedAlldayEvents: IntArray? /** - * The selection modes are HIDDEN, PRESSED, SELECTED, and LONGPRESS. + * The number of allDay events at which point we start hiding allDay events. */ - private static final int SELECTION_HIDDEN = 0; - private static final int SELECTION_PRESSED = 1; // D-pad down but not up yet - private static final int SELECTION_SELECTED = 2; - private static final int SELECTION_LONGPRESS = 3; - - private int mSelectionMode = SELECTION_HIDDEN; - - private boolean mScrolling = false; + private var mMaxUnexpandedAlldayEventCount = 4 + protected var mNumDays = 7 + private var mNumHours = 10 + + /** Width of the time line (list of hours) to the left. */ + private var mHoursWidth = 0 + private var mDateStrWidth = 0 + + /** Top of the scrollable region i.e. below date labels and all day events */ + private var mFirstCell = 0 + + /** First fully visible hour */ + private var mFirstHour = -1 + + /** Distance between the mFirstCell and the top of first fully visible hour. */ + private var mFirstHourOffset = 0 + private var mHourStrs: Array + private var mDayStrs: Array + private var mDayStrs2Letter: Array + private var mIs24HourFormat = false + private val mSelectedEvents: ArrayList = ArrayList() + private var mComputeSelectedEvents = false + private var mUpdateToast = false + private var mSelectedEvent: Event? = null + private var mPrevSelectedEvent: Event? = null + private val mPrevBox: Rect = Rect() + protected val mResources: Resources + protected val mCurrentTimeLine: Drawable + protected val mCurrentTimeAnimateLine: Drawable + protected val mTodayHeaderDrawable: Drawable + protected val mExpandAlldayDrawable: Drawable + protected val mCollapseAlldayDrawable: Drawable + protected var mAcceptedOrTentativeEventBoxDrawable: Drawable + private var mAmString: String? = null + private var mPmString: String? = null + var mScaleGestureDetector: ScaleGestureDetector + private var mTouchMode = TOUCH_MODE_INITIAL_STATE + private var mSelectionMode = SELECTION_HIDDEN + private var mScrolling = false // Pixels scrolled - private float mInitialScrollX; - private float mInitialScrollY; - - private boolean mAnimateToday = false; - private int mAnimateTodayAlpha = 0; + private var mInitialScrollX = 0f + private var mInitialScrollY = 0f + private var mAnimateToday = false + private var mAnimateTodayAlpha = 0 // Animates the height of the allday region - ObjectAnimator mAlldayAnimator; + var mAlldayAnimator: ObjectAnimator? = null + // Animates the height of events in the allday region - ObjectAnimator mAlldayEventAnimator; + var mAlldayEventAnimator: ObjectAnimator? = null + // Animates the transparency of the more events text - ObjectAnimator mMoreAlldayEventsAnimator; + var mMoreAlldayEventsAnimator: ObjectAnimator? = null + // Animates the current time marker when Today is pressed - ObjectAnimator mTodayAnimator; - // whether or not an event is stopping because it was cancelled - private boolean mCancellingAnimations = false; - // tracks whether a touch originated in the allday area - private boolean mTouchStartedInAlldayArea = false; - - private final CalendarController mController; - private final ViewSwitcher mViewSwitcher; - private final GestureDetector mGestureDetector; - private final OverScroller mScroller; - private final EdgeEffect mEdgeEffectTop; - private final EdgeEffect mEdgeEffectBottom; - private boolean mCallEdgeEffectOnAbsorb; - private final int OVERFLING_DISTANCE; - private float mLastVelocity; - - private final ScrollInterpolator mHScrollInterpolator; - private AccessibilityManager mAccessibilityMgr = null; - private boolean mIsAccessibilityEnabled = false; - private boolean mTouchExplorationEnabled = false; - private final String mNewEventHintString; - - public DayView(Context context, CalendarController controller, - ViewSwitcher viewSwitcher, EventLoader eventLoader, int numDays) { - super(context); - mContext = context; - initAccessibilityVariables(); - - mResources = context.getResources(); - mNewEventHintString = mResources.getString(R.string.day_view_new_event_hint); - mNumDays = numDays; - - DATE_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.date_header_text_size); - DAY_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.day_label_text_size); - ONE_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.one_day_header_height); - DAY_HEADER_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.day_header_bottom_margin); - EXPAND_ALL_DAY_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.all_day_bottom_margin); - HOURS_TEXT_SIZE = (int) mResources.getDimension(R.dimen.hours_text_size); - AMPM_TEXT_SIZE = (int) mResources.getDimension(R.dimen.ampm_text_size); - MIN_HOURS_WIDTH = (int) mResources.getDimension(R.dimen.min_hours_width); - HOURS_LEFT_MARGIN = (int) mResources.getDimension(R.dimen.hours_left_margin); - HOURS_RIGHT_MARGIN = (int) mResources.getDimension(R.dimen.hours_right_margin); - MULTI_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.day_header_height); - int eventTextSizeId; - if (mNumDays == 1) { - eventTextSizeId = R.dimen.day_view_event_text_size; - } else { - eventTextSizeId = R.dimen.week_view_event_text_size; - } - EVENT_TEXT_FONT_SIZE = (int) mResources.getDimension(eventTextSizeId); - NEW_EVENT_HINT_FONT_SIZE = (int) mResources.getDimension(R.dimen.new_event_hint_text_size); - MIN_EVENT_HEIGHT = mResources.getDimension(R.dimen.event_min_height); - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = MIN_EVENT_HEIGHT; - EVENT_TEXT_TOP_MARGIN = (int) mResources.getDimension(R.dimen.event_text_vertical_margin); - EVENT_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN; - EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN; - EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN; - - EVENT_TEXT_LEFT_MARGIN = (int) mResources - .getDimension(R.dimen.event_text_horizontal_margin); - EVENT_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN; - EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN; - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN; - - if (mScale == 0) { - - mScale = mResources.getDisplayMetrics().density; - if (mScale != 1) { - SINGLE_ALLDAY_HEIGHT *= mScale; - ALLDAY_TOP_MARGIN *= mScale; - MAX_HEIGHT_OF_ONE_ALLDAY_EVENT *= mScale; - - NORMAL_FONT_SIZE *= mScale; - GRID_LINE_LEFT_MARGIN *= mScale; - HOURS_TOP_MARGIN *= mScale; - MIN_CELL_WIDTH_FOR_TEXT *= mScale; - MAX_UNEXPANDED_ALLDAY_HEIGHT *= mScale; - mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; - - CURRENT_TIME_LINE_SIDE_BUFFER *= mScale; - CURRENT_TIME_LINE_TOP_OFFSET *= mScale; - - MIN_Y_SPAN *= mScale; - MAX_CELL_HEIGHT *= mScale; - DEFAULT_CELL_HEIGHT *= mScale; - DAY_HEADER_HEIGHT *= mScale; - DAY_HEADER_RIGHT_MARGIN *= mScale; - DAY_HEADER_ONE_DAY_LEFT_MARGIN *= mScale; - DAY_HEADER_ONE_DAY_RIGHT_MARGIN *= mScale; - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN *= mScale; - CALENDAR_COLOR_SQUARE_SIZE *= mScale; - EVENT_RECT_TOP_MARGIN *= mScale; - EVENT_RECT_BOTTOM_MARGIN *= mScale; - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN *= mScale; - EVENT_RECT_LEFT_MARGIN *= mScale; - EVENT_RECT_RIGHT_MARGIN *= mScale; - EVENT_RECT_STROKE_WIDTH *= mScale; - EVENT_SQUARE_WIDTH *= mScale; - EVENT_LINE_PADDING *= mScale; - NEW_EVENT_MARGIN *= mScale; - NEW_EVENT_WIDTH *= mScale; - NEW_EVENT_MAX_LENGTH *= mScale; - } - } - HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN; - DAY_HEADER_HEIGHT = mNumDays == 1 ? ONE_DAY_HEADER_HEIGHT : MULTI_DAY_HEADER_HEIGHT; - - mCurrentTimeLine = mResources.getDrawable(R.drawable.timeline_indicator_holo_light); - mCurrentTimeAnimateLine = mResources - .getDrawable(R.drawable.timeline_indicator_activated_holo_light); - mTodayHeaderDrawable = mResources.getDrawable(R.drawable.today_blue_week_holo_light); - mExpandAlldayDrawable = mResources.getDrawable(R.drawable.ic_expand_holo_light); - mCollapseAlldayDrawable = mResources.getDrawable(R.drawable.ic_collapse_holo_light); - mNewEventHintColor = mResources.getColor(R.color.new_event_hint_text_color); - mAcceptedOrTentativeEventBoxDrawable = mResources - .getDrawable(R.drawable.panel_month_event_holo_light); - - mEventLoader = eventLoader; - mEventGeometry = new EventGeometry(); - mEventGeometry.setMinEventHeight(MIN_EVENT_HEIGHT); - mEventGeometry.setHourGap(HOUR_GAP); - mEventGeometry.setCellMargin(DAY_GAP); - mLastPopupEventID = INVALID_EVENT_ID; - mController = controller; - mViewSwitcher = viewSwitcher; - mGestureDetector = new GestureDetector(context, new CalendarGestureListener()); - mScaleGestureDetector = new ScaleGestureDetector(getContext(), this); - if (mCellHeight == 0) { - mCellHeight = Utils.getSharedPreference(mContext, - GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, DEFAULT_CELL_HEIGHT); - } - mScroller = new OverScroller(context); - mHScrollInterpolator = new ScrollInterpolator(); - mEdgeEffectTop = new EdgeEffect(context); - mEdgeEffectBottom = new EdgeEffect(context); - ViewConfiguration vc = ViewConfiguration.get(context); - mScaledPagingTouchSlop = vc.getScaledPagingTouchSlop(); - mOnDownDelay = ViewConfiguration.getTapTimeout(); - OVERFLING_DISTANCE = vc.getScaledOverflingDistance(); + var mTodayAnimator: ObjectAnimator? = null - init(context); - } + // whether or not an event is stopping because it was cancelled + private var mCancellingAnimations = false + // tracks whether a touch originated in the allday area + private var mTouchStartedInAlldayArea = false + private val mController: CalendarController + private val mViewSwitcher: ViewSwitcher + private val mGestureDetector: GestureDetector + private val mScroller: OverScroller + private val mEdgeEffectTop: EdgeEffect + private val mEdgeEffectBottom: EdgeEffect + private var mCallEdgeEffectOnAbsorb = false + private val OVERFLING_DISTANCE: Int + private var mLastVelocity = 0f + private val mHScrollInterpolator: ScrollInterpolator + private var mAccessibilityMgr: AccessibilityManager? = null + private var mIsAccessibilityEnabled = false + private var mTouchExplorationEnabled = false + private val mNewEventHintString: String @Override - protected void onAttachedToWindow() { + protected fun onAttachedToWindow() { if (mHandler == null) { - mHandler = getHandler(); - mHandler.post(mUpdateCurrentTime); + mHandler = getHandler() + mHandler.post(mUpdateCurrentTime) } } - private void init(Context context) { - setFocusable(true); + private fun init(context: Context) { + setFocusable(true) // Allow focus in touch mode so that we can do keyboard shortcuts // even after we've entered touch mode. - setFocusableInTouchMode(true); - setClickable(true); - setOnCreateContextMenuListener(this); - - mFirstDayOfWeek = Utils.getFirstDayOfWeek(context); - - mCurrentTime = new Time(Utils.getTimeZone(context, mTZUpdater)); - long currentTime = System.currentTimeMillis(); - mCurrentTime.set(currentTime); - mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff); - - mWeek_saturdayColor = mResources.getColor(R.color.week_saturday); - mWeek_sundayColor = mResources.getColor(R.color.week_sunday); - mCalendarDateBannerTextColor = mResources.getColor(R.color.calendar_date_banner_text_color); - mFutureBgColorRes = mResources.getColor(R.color.calendar_future_bg_color); - mBgColor = mResources.getColor(R.color.calendar_hour_background); - mCalendarAmPmLabel = mResources.getColor(R.color.calendar_ampm_label); - mCalendarGridAreaSelected = mResources.getColor(R.color.calendar_grid_area_selected); + setFocusableInTouchMode(true) + setClickable(true) + setOnCreateContextMenuListener(this) + mFirstDayOfWeek = Utils.getFirstDayOfWeek(context) + mCurrentTime = Time(Utils.getTimeZone(context, mTZUpdater)) + val currentTime: Long = System.currentTimeMillis() + mCurrentTime.set(currentTime) + mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff) + mWeek_saturdayColor = mResources.getColor(R.color.week_saturday) + mWeek_sundayColor = mResources.getColor(R.color.week_sunday) + mCalendarDateBannerTextColor = mResources.getColor(R.color.calendar_date_banner_text_color) + mFutureBgColorRes = mResources.getColor(R.color.calendar_future_bg_color) + mBgColor = mResources.getColor(R.color.calendar_hour_background) + mCalendarAmPmLabel = mResources.getColor(R.color.calendar_ampm_label) + mCalendarGridAreaSelected = mResources.getColor(R.color.calendar_grid_area_selected) mCalendarGridLineInnerHorizontalColor = mResources - .getColor(R.color.calendar_grid_line_inner_horizontal_color); + .getColor(R.color.calendar_grid_line_inner_horizontal_color) mCalendarGridLineInnerVerticalColor = mResources - .getColor(R.color.calendar_grid_line_inner_vertical_color); - mCalendarHourLabelColor = mResources.getColor(R.color.calendar_hour_label); - mEventTextColor = mResources.getColor(R.color.calendar_event_text_color); - mMoreEventsTextColor = mResources.getColor(R.color.month_event_other_color); - - mEventTextPaint.setTextSize(EVENT_TEXT_FONT_SIZE); - mEventTextPaint.setTextAlign(Paint.Align.LEFT); - mEventTextPaint.setAntiAlias(true); - - int gridLineColor = mResources.getColor(R.color.calendar_grid_line_highlight_color); - Paint p = mSelectionPaint; - p.setColor(gridLineColor); - p.setStyle(Style.FILL); - p.setAntiAlias(false); - - p = mPaint; - p.setAntiAlias(true); + .getColor(R.color.calendar_grid_line_inner_vertical_color) + mCalendarHourLabelColor = mResources.getColor(R.color.calendar_hour_label) + mEventTextColor = mResources.getColor(R.color.calendar_event_text_color) + mMoreEventsTextColor = mResources.getColor(R.color.month_event_other_color) + mEventTextPaint.setTextSize(EVENT_TEXT_FONT_SIZE) + mEventTextPaint.setTextAlign(Paint.Align.LEFT) + mEventTextPaint.setAntiAlias(true) + val gridLineColor: Int = mResources.getColor(R.color.calendar_grid_line_highlight_color) + var p: Paint = mSelectionPaint + p.setColor(gridLineColor) + p.setStyle(Style.FILL) + p.setAntiAlias(false) + p = mPaint + p.setAntiAlias(true) // Allocate space for 2 weeks worth of weekday names so that we can // easily start the week display at any week day. - mDayStrs = new String[14]; + mDayStrs = arrayOfNulls(14) // Also create an array of 2-letter abbreviations. - mDayStrs2Letter = new String[14]; - - for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { - int index = i - Calendar.SUNDAY; + mDayStrs2Letter = arrayOfNulls(14) + for (i in Calendar.SUNDAY..Calendar.SATURDAY) { + val index: Int = i - Calendar.SUNDAY // e.g. Tue for Tuesday mDayStrs[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_MEDIUM) - .toUpperCase(); - mDayStrs[index + 7] = mDayStrs[index]; + .toUpperCase() + mDayStrs[index + 7] = mDayStrs[index] // e.g. Tu for Tuesday mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORT) - .toUpperCase(); + .toUpperCase() // If we don't have 2-letter day strings, fall back to 1-letter. - if (mDayStrs2Letter[index].equals(mDayStrs[index])) { - mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORTEST); + if (mDayStrs2Letter[index]!!.equals(mDayStrs[index])) { + mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORTEST) } - - mDayStrs2Letter[index + 7] = mDayStrs2Letter[index]; + mDayStrs2Letter[index + 7] = mDayStrs2Letter[index] } // Figure out how much space we need for the 3-letter abbrev names // in the worst case. - p.setTextSize(DATE_HEADER_FONT_SIZE); - p.setTypeface(mBold); - String[] dateStrs = {" 28", " 30"}; - mDateStrWidth = computeMaxStringWidth(0, dateStrs, p); - p.setTextSize(DAY_HEADER_FONT_SIZE); - mDateStrWidth += computeMaxStringWidth(0, mDayStrs, p); - - p.setTextSize(HOURS_TEXT_SIZE); - p.setTypeface(null); - handleOnResume(); - - mAmString = DateUtils.getAMPMString(Calendar.AM).toUpperCase(); - mPmString = DateUtils.getAMPMString(Calendar.PM).toUpperCase(); - String[] ampm = {mAmString, mPmString}; - p.setTextSize(AMPM_TEXT_SIZE); - mHoursWidth = Math.max(HOURS_MARGIN, computeMaxStringWidth(mHoursWidth, ampm, p) - + HOURS_RIGHT_MARGIN); - mHoursWidth = Math.max(MIN_HOURS_WIDTH, mHoursWidth); - - LayoutInflater inflater; - inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mPopupView = inflater.inflate(R.layout.bubble_event, null); - mPopupView.setLayoutParams(new ViewGroup.LayoutParams( + p.setTextSize(DATE_HEADER_FONT_SIZE) + p.setTypeface(mBold) + val dateStrs = arrayOf(" 28", " 30") + mDateStrWidth = computeMaxStringWidth(0, dateStrs, p) + p.setTextSize(DAY_HEADER_FONT_SIZE) + mDateStrWidth += computeMaxStringWidth(0, mDayStrs, p) + p.setTextSize(HOURS_TEXT_SIZE) + p.setTypeface(null) + handleOnResume() + mAmString = DateUtils.getAMPMString(Calendar.AM).toUpperCase() + mPmString = DateUtils.getAMPMString(Calendar.PM).toUpperCase() + val ampm = arrayOf(mAmString, mPmString) + p.setTextSize(AMPM_TEXT_SIZE) + mHoursWidth = Math.max( + HOURS_MARGIN, computeMaxStringWidth(mHoursWidth, ampm, p) + + HOURS_RIGHT_MARGIN + ) + mHoursWidth = Math.max(MIN_HOURS_WIDTH, mHoursWidth) + val inflater: LayoutInflater + inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + mPopupView = inflater.inflate(R.layout.bubble_event, null) + mPopupView.setLayoutParams( + LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - mPopup = new PopupWindow(context); - mPopup.setContentView(mPopupView); - Resources.Theme dialogTheme = getResources().newTheme(); - dialogTheme.applyStyle(android.R.style.Theme_Dialog, true); - TypedArray ta = dialogTheme.obtainStyledAttributes(new int[] { - android.R.attr.windowBackground }); - mPopup.setBackgroundDrawable(ta.getDrawable(0)); - ta.recycle(); + ViewGroup.LayoutParams.WRAP_CONTENT + ) + ) + mPopup = PopupWindow(context) + mPopup.setContentView(mPopupView) + val dialogTheme: Resources.Theme = getResources().newTheme() + dialogTheme.applyStyle(android.R.style.Theme_Dialog, true) + val ta: TypedArray = dialogTheme.obtainStyledAttributes( + intArrayOf( + android.R.attr.windowBackground + ) + ) + mPopup.setBackgroundDrawable(ta.getDrawable(0)) + ta.recycle() // Enable touching the popup window - mPopupView.setOnClickListener(this); + mPopupView.setOnClickListener(this) // Catch long clicks for creating a new event - setOnLongClickListener(this); - - mBaseDate = new Time(Utils.getTimeZone(context, mTZUpdater)); - long millis = System.currentTimeMillis(); - mBaseDate.set(millis); - - mEarliestStartHour = new int[mNumDays]; - mHasAllDayEvent = new boolean[mNumDays]; + setOnLongClickListener(this) + mBaseDate = Time(Utils.getTimeZone(context, mTZUpdater)) + val millis: Long = System.currentTimeMillis() + mBaseDate.set(millis) + mEarliestStartHour = IntArray(mNumDays) + mHasAllDayEvent = BooleanArray(mNumDays) // mLines is the array of points used with Canvas.drawLines() in // drawGridBackground() and drawAllDayEvents(). Its size depends // on the max number of lines that can ever be drawn by any single // drawLines() call in either of those methods. - final int maxGridLines = (24 + 1) // max horizontal lines we might draw - + (mNumDays + 1); // max vertical lines we might draw - mLines = new float[maxGridLines * 4]; + val maxGridLines = (24 + 1 // max horizontal lines we might draw + + (mNumDays + 1)) // max vertical lines we might draw + mLines = FloatArray(maxGridLines * 4) } /** * This is called when the popup window is pressed. */ - public void onClick(View v) { - if (v == mPopupView) { + fun onClick(v: View) { + if (v === mPopupView) { // Pretend it was a trackball click because that will always // jump to the "View event" screen. - switchViews(true /* trackball */); + switchViews(true /* trackball */) } } - public void handleOnResume() { - initAccessibilityVariables(); - if(Utils.getSharedPreference(mContext, OtherPreferences.KEY_OTHER_1, false)) { - mFutureBgColor = 0; + fun handleOnResume() { + initAccessibilityVariables() + if (Utils.getSharedPreference(mContext, OtherPreferences.KEY_OTHER_1, false)) { + mFutureBgColor = 0 } else { - mFutureBgColor = mFutureBgColorRes; - } - mIs24HourFormat = DateFormat.is24HourFormat(mContext); - mHourStrs = mIs24HourFormat ? CalendarData.s24Hours : CalendarData.s12HoursNoAmPm; - mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - mLastSelectionDayForAccessibility = 0; - mLastSelectionHourForAccessibility = 0; - mLastSelectedEventForAccessibility = null; - mSelectionMode = SELECTION_HIDDEN; - } - - private void initAccessibilityVariables() { - mAccessibilityMgr = (AccessibilityManager) mContext - .getSystemService(Service.ACCESSIBILITY_SERVICE); - mIsAccessibilityEnabled = mAccessibilityMgr != null && mAccessibilityMgr.isEnabled(); - mTouchExplorationEnabled = isTouchExplorationEnabled(); - } - + mFutureBgColor = mFutureBgColorRes + } + mIs24HourFormat = DateFormat.is24HourFormat(mContext) + mHourStrs = if (mIs24HourFormat) CalendarData.s24Hours else CalendarData.s12HoursNoAmPm + mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext) + mLastSelectionDayForAccessibility = 0 + mLastSelectionHourForAccessibility = 0 + mLastSelectedEventForAccessibility = null + mSelectionMode = SELECTION_HIDDEN + } + + private fun initAccessibilityVariables() { + mAccessibilityMgr = mContext + .getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager + mIsAccessibilityEnabled = mAccessibilityMgr != null && mAccessibilityMgr.isEnabled() + mTouchExplorationEnabled = isTouchExplorationEnabled + }/* ignore isDst */// We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. /** * Returns the start of the selected time in milliseconds since the epoch. * * @return selected time in UTC milliseconds since the epoch. */ - long getSelectedTimeInMillis() { - Time time = new Time(mBaseDate); - time.setJulianDay(mSelectionDay); - time.hour = mSelectionHour; - - // We ignore the "isDst" field because we want normalize() to figure - // out the correct DST value and not adjust the selected time based - // on the current setting of DST. - return time.normalize(true /* ignore isDst */); - } - - Time getSelectedTime() { - Time time = new Time(mBaseDate); - time.setJulianDay(mSelectionDay); - time.hour = mSelectionHour; - - // We ignore the "isDst" field because we want normalize() to figure - // out the correct DST value and not adjust the selected time based - // on the current setting of DST. - time.normalize(true /* ignore isDst */); - return time; - } - - Time getSelectedTimeForAccessibility() { - Time time = new Time(mBaseDate); - time.setJulianDay(mSelectionDayForAccessibility); - time.hour = mSelectionHourForAccessibility; - - // We ignore the "isDst" field because we want normalize() to figure - // out the correct DST value and not adjust the selected time based - // on the current setting of DST. - time.normalize(true /* ignore isDst */); - return time; - } + val selectedTimeInMillis: Long + get() { + val time = Time(mBaseDate) + time.setJulianDay(mSelectionDay) + time.hour = mSelectionHour + + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + return time.normalize(true /* ignore isDst */) + }/* ignore isDst */ + + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + val selectedTime: Time + get() { + val time = Time(mBaseDate) + time.setJulianDay(mSelectionDay) + time.hour = mSelectionHour + + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + time.normalize(true /* ignore isDst */) + return time + }/* ignore isDst */ + + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + val selectedTimeForAccessibility: Time + get() { + val time = Time(mBaseDate) + time.setJulianDay(mSelectionDayForAccessibility) + time.hour = mSelectionHourForAccessibility + + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + time.normalize(true /* ignore isDst */) + return time + } /** * Returns the start of the selected time in minutes since midnight, * local time. The derived class must ensure that this is consistent * with the return value from getSelectedTimeInMillis(). */ - int getSelectedMinutesSinceMidnight() { - return mSelectionHour * MINUTES_PER_HOUR; - } - - int getFirstVisibleHour() { - return mFirstHour; - } - - void setFirstVisibleHour(int firstHour) { - mFirstHour = firstHour; - mFirstHourOffset = 0; - } - - public void setSelected(Time time, boolean ignoreTime, boolean animateToday) { - mBaseDate.set(time); - setSelectedHour(mBaseDate.hour); - setSelectedEvent(null); - mPrevSelectedEvent = null; - long millis = mBaseDate.toMillis(false /* use isDst */); - setSelectedDay(Time.getJulianDay(millis, mBaseDate.gmtoff)); - mSelectedEvents.clear(); - mComputeSelectedEvents = true; - - int gotoY = Integer.MIN_VALUE; - + val selectedMinutesSinceMidnight: Int + get() = mSelectionHour * MINUTES_PER_HOUR + var firstVisibleHour: Int + get() = mFirstHour + set(firstHour) { + mFirstHour = firstHour + mFirstHourOffset = 0 + } + + fun setSelected(time: Time?, ignoreTime: Boolean, animateToday: Boolean) { + mBaseDate.set(time) + setSelectedHour(mBaseDate.hour) + setSelectedEvent(null) + mPrevSelectedEvent = null + val millis: Long = mBaseDate.toMillis(false /* use isDst */) + selectedDay = Time.getJulianDay(millis, mBaseDate.gmtoff) + mSelectedEvents.clear() + mComputeSelectedEvents = true + var gotoY: Int = Integer.MIN_VALUE if (!ignoreTime && mGridAreaHeight != -1) { - int lastHour = 0; - + var lastHour = 0 if (mBaseDate.hour < mFirstHour) { // Above visible region - gotoY = mBaseDate.hour * (mCellHeight + HOUR_GAP); + gotoY = mBaseDate.hour * (mCellHeight + HOUR_GAP) } else { - lastHour = (mGridAreaHeight - mFirstHourOffset) / (mCellHeight + HOUR_GAP) - + mFirstHour; - + lastHour = ((mGridAreaHeight - mFirstHourOffset) / (mCellHeight + HOUR_GAP) + + mFirstHour) if (mBaseDate.hour >= lastHour) { // Below visible region // target hour + 1 (to give it room to see the event) - // grid height (to get the y of the top of the visible // region) - gotoY = (int) ((mBaseDate.hour + 1 + mBaseDate.minute / 60.0f) - * (mCellHeight + HOUR_GAP) - mGridAreaHeight); + gotoY = ((mBaseDate.hour + 1 + mBaseDate.minute / 60.0f) + * (mCellHeight + HOUR_GAP) - mGridAreaHeight) } } - if (DEBUG) { - Log.e(TAG, "Go " + gotoY + " 1st " + mFirstHour + ":" + mFirstHourOffset + "CH " - + (mCellHeight + HOUR_GAP) + " lh " + lastHour + " gh " + mGridAreaHeight - + " ymax " + mMaxViewStartY); + Log.e( + TAG, "Go " + gotoY + " 1st " + mFirstHour + ":" + mFirstHourOffset + "CH " + + (mCellHeight + HOUR_GAP) + " lh " + lastHour + " gh " + mGridAreaHeight + + " ymax " + mMaxViewStartY + ) } - if (gotoY > mMaxViewStartY) { - gotoY = mMaxViewStartY; + gotoY = mMaxViewStartY } else if (gotoY < 0 && gotoY != Integer.MIN_VALUE) { - gotoY = 0; + gotoY = 0 } } - - recalc(); - - mRemeasure = true; - invalidate(); - - boolean delayAnimateToday = false; + recalc() + mRemeasure = true + invalidate() + var delayAnimateToday = false if (gotoY != Integer.MIN_VALUE) { - ValueAnimator scrollAnim = ObjectAnimator.ofInt(this, "viewStartY", mViewStartY, gotoY); - scrollAnim.setDuration(GOTO_SCROLL_DURATION); - scrollAnim.setInterpolator(new AccelerateDecelerateInterpolator()); - scrollAnim.addListener(mAnimatorListener); - scrollAnim.start(); - delayAnimateToday = true; + val scrollAnim: ValueAnimator = + ObjectAnimator.ofInt(this, "viewStartY", mViewStartY, gotoY) + scrollAnim.setDuration(GOTO_SCROLL_DURATION) + scrollAnim.setInterpolator(AccelerateDecelerateInterpolator()) + scrollAnim.addListener(mAnimatorListener) + scrollAnim.start() + delayAnimateToday = true } if (animateToday) { - synchronized (mTodayAnimatorListener) { + synchronized(mTodayAnimatorListener) { if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners(); - mTodayAnimator.cancel(); + mTodayAnimator.removeAllListeners() + mTodayAnimator.cancel() } - mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha", - mAnimateTodayAlpha, 255); - mAnimateToday = true; - mTodayAnimatorListener.setFadingIn(true); - mTodayAnimatorListener.setAnimator(mTodayAnimator); - mTodayAnimator.addListener(mTodayAnimatorListener); - mTodayAnimator.setDuration(150); + mTodayAnimator = ObjectAnimator.ofInt( + this, "animateTodayAlpha", + mAnimateTodayAlpha, 255 + ) + mAnimateToday = true + mTodayAnimatorListener.setFadingIn(true) + mTodayAnimatorListener.setAnimator(mTodayAnimator) + mTodayAnimator.addListener(mTodayAnimatorListener) + mTodayAnimator.setDuration(150) if (delayAnimateToday) { - mTodayAnimator.setStartDelay(GOTO_SCROLL_DURATION); + mTodayAnimator.setStartDelay(GOTO_SCROLL_DURATION) } - mTodayAnimator.start(); + mTodayAnimator.start() } } - sendAccessibilityEventAsNeeded(false); + sendAccessibilityEventAsNeeded(false) } // Called from animation framework via reflection. Do not remove - public void setViewStartY(int viewStartY) { + fun setViewStartY(viewStartY: Int) { + var viewStartY = viewStartY if (viewStartY > mMaxViewStartY) { - viewStartY = mMaxViewStartY; - } - - mViewStartY = viewStartY; - - computeFirstHour(); - invalidate(); - } - - public void setAnimateTodayAlpha(int todayAlpha) { - mAnimateTodayAlpha = todayAlpha; - invalidate(); - } - - public Time getSelectedDay() { - Time time = new Time(mBaseDate); - time.setJulianDay(mSelectionDay); - time.hour = mSelectionHour; - - // We ignore the "isDst" field because we want normalize() to figure - // out the correct DST value and not adjust the selected time based - // on the current setting of DST. - time.normalize(true /* ignore isDst */); - return time; - } - - public void updateTitle() { - Time start = new Time(mBaseDate); - start.normalize(true); - Time end = new Time(start); - end.monthDay += mNumDays - 1; + viewStartY = mMaxViewStartY + } + mViewStartY = viewStartY + computeFirstHour() + invalidate() + } + + fun setAnimateTodayAlpha(todayAlpha: Int) { + mAnimateTodayAlpha = todayAlpha + invalidate() + }/* ignore isDst */ + + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + var selectedDay: Time + get() { + val time = Time(mBaseDate) + time.setJulianDay(mSelectionDay) + time.hour = mSelectionHour + + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + time.normalize(true /* ignore isDst */) + return time + } + private set(d) { + mSelectionDay = d + mSelectionDayForAccessibility = d + } + + fun updateTitle() { + val start = Time(mBaseDate) + start.normalize(true) + val end = Time(start) + end.monthDay += mNumDays - 1 // Move it forward one minute so the formatter doesn't lose a day - end.minute += 1; - end.normalize(true); - - long formatFlags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; + end.minute += 1 + end.normalize(true) + var formatFlags: Long = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_YEAR if (mNumDays != 1) { // Don't show day of the month if for multi-day view - formatFlags |= DateUtils.FORMAT_NO_MONTH_DAY; + formatFlags = formatFlags or DateUtils.FORMAT_NO_MONTH_DAY // Abbreviate the month if showing multiple months - if (start.month != end.month) { - formatFlags |= DateUtils.FORMAT_ABBREV_MONTH; + if (start.month !== end.month) { + formatFlags = formatFlags or DateUtils.FORMAT_ABBREV_MONTH } } - - mController.sendEvent(this, EventType.UPDATE_TITLE, start, end, null, -1, ViewType.CURRENT, - formatFlags, null, null); + mController.sendEvent( + this, EventType.UPDATE_TITLE, start, end, null, -1, ViewType.CURRENT, + formatFlags, null, null + ) } /** @@ -1127,139 +728,130 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * range, a positive number if "time" is after the visible time range, and 0 * if it is in the visible time range. */ - public int compareToVisibleTimeRange(Time time) { - - int savedHour = mBaseDate.hour; - int savedMinute = mBaseDate.minute; - int savedSec = mBaseDate.second; - - mBaseDate.hour = 0; - mBaseDate.minute = 0; - mBaseDate.second = 0; - + fun compareToVisibleTimeRange(time: Time): Int { + val savedHour: Int = mBaseDate.hour + val savedMinute: Int = mBaseDate.minute + val savedSec: Int = mBaseDate.second + mBaseDate.hour = 0 + mBaseDate.minute = 0 + mBaseDate.second = 0 if (DEBUG) { - Log.d(TAG, "Begin " + mBaseDate.toString()); - Log.d(TAG, "Diff " + time.toString()); + Log.d(TAG, "Begin " + mBaseDate.toString()) + Log.d(TAG, "Diff " + time.toString()) } // Compare beginning of range - int diff = Time.compare(time, mBaseDate); + var diff: Int = Time.compare(time, mBaseDate) if (diff > 0) { // Compare end of range - mBaseDate.monthDay += mNumDays; - mBaseDate.normalize(true); - diff = Time.compare(time, mBaseDate); - - if (DEBUG) Log.d(TAG, "End " + mBaseDate.toString()); - - mBaseDate.monthDay -= mNumDays; - mBaseDate.normalize(true); + mBaseDate.monthDay += mNumDays + mBaseDate.normalize(true) + diff = Time.compare(time, mBaseDate) + if (DEBUG) Log.d(TAG, "End " + mBaseDate.toString()) + mBaseDate.monthDay -= mNumDays + mBaseDate.normalize(true) if (diff < 0) { // in visible time - diff = 0; + diff = 0 } else if (diff == 0) { // Midnight of following day - diff = 1; + diff = 1 } } - - if (DEBUG) Log.d(TAG, "Diff: " + diff); - - mBaseDate.hour = savedHour; - mBaseDate.minute = savedMinute; - mBaseDate.second = savedSec; - return diff; + if (DEBUG) Log.d(TAG, "Diff: $diff") + mBaseDate.hour = savedHour + mBaseDate.minute = savedMinute + mBaseDate.second = savedSec + return diff } - private void recalc() { + private fun recalc() { // Set the base date to the beginning of the week if we are displaying // 7 days at a time. if (mNumDays == 7) { - adjustToBeginningOfWeek(mBaseDate); + adjustToBeginningOfWeek(mBaseDate) } - - final long start = mBaseDate.toMillis(false /* use isDst */); - mFirstJulianDay = Time.getJulianDay(start, mBaseDate.gmtoff); - mLastJulianDay = mFirstJulianDay + mNumDays - 1; - - mMonthLength = mBaseDate.getActualMaximum(Time.MONTH_DAY); - mFirstVisibleDate = mBaseDate.monthDay; - mFirstVisibleDayOfWeek = mBaseDate.weekDay; + val start: Long = mBaseDate.toMillis(false /* use isDst */) + mFirstJulianDay = Time.getJulianDay(start, mBaseDate.gmtoff) + mLastJulianDay = mFirstJulianDay + mNumDays - 1 + mMonthLength = mBaseDate.getActualMaximum(Time.MONTH_DAY) + mFirstVisibleDate = mBaseDate.monthDay + mFirstVisibleDayOfWeek = mBaseDate.weekDay } - private void adjustToBeginningOfWeek(Time time) { - int dayOfWeek = time.weekDay; - int diff = dayOfWeek - mFirstDayOfWeek; + private fun adjustToBeginningOfWeek(time: Time?) { + val dayOfWeek: Int = time.weekDay + var diff = dayOfWeek - mFirstDayOfWeek if (diff != 0) { if (diff < 0) { - diff += 7; + diff += 7 } - time.monthDay -= diff; - time.normalize(true /* ignore isDst */); + time.monthDay -= diff + time.normalize(true /* ignore isDst */) } } @Override - protected void onSizeChanged(int width, int height, int oldw, int oldh) { - mViewWidth = width; - mViewHeight = height; - mEdgeEffectTop.setSize(mViewWidth, mViewHeight); - mEdgeEffectBottom.setSize(mViewWidth, mViewHeight); - int gridAreaWidth = width - mHoursWidth; - mCellWidth = (gridAreaWidth - (mNumDays * DAY_GAP)) / mNumDays; + protected fun onSizeChanged(width: Int, height: Int, oldw: Int, oldh: Int) { + mViewWidth = width + mViewHeight = height + mEdgeEffectTop.setSize(mViewWidth, mViewHeight) + mEdgeEffectBottom.setSize(mViewWidth, mViewHeight) + val gridAreaWidth = width - mHoursWidth + mCellWidth = (gridAreaWidth - mNumDays * DAY_GAP) / mNumDays // This would be about 1 day worth in a 7 day view - mHorizontalSnapBackThreshold = width / 7; - - Paint p = new Paint(); - p.setTextSize(HOURS_TEXT_SIZE); - mHoursTextHeight = (int) Math.abs(p.ascent()); - remeasure(width, height); + mHorizontalSnapBackThreshold = width / 7 + val p = Paint() + p.setTextSize(HOURS_TEXT_SIZE) + mHoursTextHeight = Math.abs(p.ascent()) + remeasure(width, height) } /** * Measures the space needed for various parts of the view after * loading new events. This can change if there are all-day events. */ - private void remeasure(int width, int height) { + private fun remeasure(width: Int, height: Int) { // Shrink to fit available space but make sure we can display at least two events - MAX_UNEXPANDED_ALLDAY_HEIGHT = (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4); - MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.min(MAX_UNEXPANDED_ALLDAY_HEIGHT, height / 6); - MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.max(MAX_UNEXPANDED_ALLDAY_HEIGHT, - (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 2); + MAX_UNEXPANDED_ALLDAY_HEIGHT = (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4).toInt() + MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.min(MAX_UNEXPANDED_ALLDAY_HEIGHT, height / 6) + MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.max( + MAX_UNEXPANDED_ALLDAY_HEIGHT, + MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt() * 2 + ) mMaxUnexpandedAlldayEventCount = - (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT / MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT); + (MAX_UNEXPANDED_ALLDAY_HEIGHT / MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt() // First, clear the array of earliest start times, and the array // indicating presence of an all-day event. - for (int day = 0; day < mNumDays; day++) { - mEarliestStartHour[day] = 25; // some big number - mHasAllDayEvent[day] = false; + for (day in 0 until mNumDays) { + mEarliestStartHour[day] = 25 // some big number + mHasAllDayEvent[day] = false } - - int maxAllDayEvents = mMaxAlldayEvents; + val maxAllDayEvents = mMaxAlldayEvents // The min is where 24 hours cover the entire visible area - mMinCellHeight = Math.max((height - DAY_HEADER_HEIGHT) / 24, (int) MIN_EVENT_HEIGHT); + mMinCellHeight = Math.max((height - DAY_HEADER_HEIGHT) / 24, MIN_EVENT_HEIGHT.toInt()) if (mCellHeight < mMinCellHeight) { - mCellHeight = mMinCellHeight; + mCellHeight = mMinCellHeight } // Calculate mAllDayHeight - mFirstCell = DAY_HEADER_HEIGHT; - int allDayHeight = 0; + mFirstCell = DAY_HEADER_HEIGHT + var allDayHeight = 0 if (maxAllDayEvents > 0) { - int maxAllAllDayHeight = height - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; + val maxAllAllDayHeight = height - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT // If there is at most one all-day event per day, then use less // space (but more than the space for a single event). if (maxAllDayEvents == 1) { - allDayHeight = SINGLE_ALLDAY_HEIGHT; - } else if (maxAllDayEvents <= mMaxUnexpandedAlldayEventCount){ + allDayHeight = SINGLE_ALLDAY_HEIGHT + } else if (maxAllDayEvents <= mMaxUnexpandedAlldayEventCount) { // Allow the all-day area to grow in height depending on the // number of all-day events we need to show, up to a limit. - allDayHeight = maxAllDayEvents * MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; + allDayHeight = maxAllDayEvents * MAX_HEIGHT_OF_ONE_ALLDAY_EVENT if (allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) { - allDayHeight = MAX_UNEXPANDED_ALLDAY_HEIGHT; + allDayHeight = MAX_UNEXPANDED_ALLDAY_HEIGHT } } else { // if we have more than the magic number, check if we're animating @@ -1268,58 +860,58 @@ public class DayView extends View implements View.OnCreateContextMenuListener, // Don't shrink the space past the final allDay space. The animation // continues to hide the last event so the more events text can // fade in. - allDayHeight = Math.max(mAnimateDayHeight, MAX_UNEXPANDED_ALLDAY_HEIGHT); + allDayHeight = Math.max(mAnimateDayHeight, MAX_UNEXPANDED_ALLDAY_HEIGHT) } else { // Try to fit all the events in - allDayHeight = (int) (maxAllDayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT); + allDayHeight = (maxAllDayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt() // But clip the area depending on which mode we're in if (!mShowAllAllDayEvents && allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) { - allDayHeight = (int) (mMaxUnexpandedAlldayEventCount * - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT); + allDayHeight = (mMaxUnexpandedAlldayEventCount * + MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt() } else if (allDayHeight > maxAllAllDayHeight) { - allDayHeight = maxAllAllDayHeight; + allDayHeight = maxAllAllDayHeight } } } - mFirstCell = DAY_HEADER_HEIGHT + allDayHeight + ALLDAY_TOP_MARGIN; + mFirstCell = DAY_HEADER_HEIGHT + allDayHeight + ALLDAY_TOP_MARGIN } else { - mSelectionAllday = false; + mSelectionAllday = false } - mAlldayHeight = allDayHeight; - - mGridAreaHeight = height - mFirstCell; + mAlldayHeight = allDayHeight + mGridAreaHeight = height - mFirstCell // Set up the expand icon position - int allDayIconWidth = mExpandAlldayDrawable.getIntrinsicWidth(); - mExpandAllDayRect.left = Math.max((mHoursWidth - allDayIconWidth) / 2, - EVENT_ALL_DAY_TEXT_LEFT_MARGIN); - mExpandAllDayRect.right = Math.min(mExpandAllDayRect.left + allDayIconWidth, mHoursWidth - - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN); - mExpandAllDayRect.bottom = mFirstCell - EXPAND_ALL_DAY_BOTTOM_MARGIN; - mExpandAllDayRect.top = mExpandAllDayRect.bottom - - mExpandAlldayDrawable.getIntrinsicHeight(); - - mNumHours = mGridAreaHeight / (mCellHeight + HOUR_GAP); - mEventGeometry.setHourHeight(mCellHeight); - - final long minimumDurationMillis = (long) - (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f)); - Event.computePositions(mEvents, minimumDurationMillis); + val allDayIconWidth: Int = mExpandAlldayDrawable.getIntrinsicWidth() + mExpandAllDayRect.left = Math.max( + (mHoursWidth - allDayIconWidth) / 2, + EVENT_ALL_DAY_TEXT_LEFT_MARGIN + ) + mExpandAllDayRect.right = Math.min( + mExpandAllDayRect.left + allDayIconWidth, mHoursWidth + - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN + ) + mExpandAllDayRect.bottom = mFirstCell - EXPAND_ALL_DAY_BOTTOM_MARGIN + mExpandAllDayRect.top = (mExpandAllDayRect.bottom + - mExpandAlldayDrawable.getIntrinsicHeight()) + mNumHours = mGridAreaHeight / (mCellHeight + HOUR_GAP) + mEventGeometry.setHourHeight(mCellHeight) + val minimumDurationMillis = + (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f)) as Long + Event.computePositions(mEvents, minimumDurationMillis) // Compute the top of our reachable view - mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight; + mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight if (DEBUG) { - Log.e(TAG, "mViewStartY: " + mViewStartY); - Log.e(TAG, "mMaxViewStartY: " + mMaxViewStartY); + Log.e(TAG, "mViewStartY: $mViewStartY") + Log.e(TAG, "mMaxViewStartY: $mMaxViewStartY") } if (mViewStartY > mMaxViewStartY) { - mViewStartY = mMaxViewStartY; - computeFirstHour(); + mViewStartY = mMaxViewStartY + computeFirstHour() } - if (mFirstHour == -1) { - initFirstHour(); - mFirstHourOffset = 0; + initFirstHour() + mFirstHourOffset = 0 } // When we change the base date, the number of all-day events may @@ -1327,17 +919,16 @@ public class DayView extends View implements View.OnCreateContextMenuListener, // we use the mFirstHourOffset from the previous view, but that may // be too large for the new view if the cell height is smaller. if (mFirstHourOffset >= mCellHeight + HOUR_GAP) { - mFirstHourOffset = mCellHeight + HOUR_GAP - 1; + mFirstHourOffset = mCellHeight + HOUR_GAP - 1 } - mViewStartY = mFirstHour * (mCellHeight + HOUR_GAP) - mFirstHourOffset; - - final int eventAreaWidth = mNumDays * (mCellWidth + DAY_GAP); + mViewStartY = mFirstHour * (mCellHeight + HOUR_GAP) - mFirstHourOffset + val eventAreaWidth = mNumDays * (mCellWidth + DAY_GAP) //When we get new events we don't want to dismiss the popup unless the event changes if (mSelectedEvent != null && mLastPopupEventID != mSelectedEvent.id) { - mPopup.dismiss(); + mPopup.dismiss() } - mPopup.setWidth(eventAreaWidth - 20); - mPopup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT); + mPopup.setWidth(eventAreaWidth - 20) + mPopup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT) } /** @@ -1348,28 +939,27 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * * @param view the view to initialize. */ - private void initView(DayView view) { - view.setSelectedHour(mSelectionHour); - view.mSelectedEvents.clear(); - view.mComputeSelectedEvents = true; - view.mFirstHour = mFirstHour; - view.mFirstHourOffset = mFirstHourOffset; - view.remeasure(getWidth(), getHeight()); - view.initAllDayHeights(); - - view.setSelectedEvent(null); - view.mPrevSelectedEvent = null; - view.mFirstDayOfWeek = mFirstDayOfWeek; + private fun initView(view: DayView) { + view.setSelectedHour(mSelectionHour) + view.mSelectedEvents.clear() + view.mComputeSelectedEvents = true + view.mFirstHour = mFirstHour + view.mFirstHourOffset = mFirstHourOffset + view.remeasure(getWidth(), getHeight()) + view.initAllDayHeights() + view.setSelectedEvent(null) + view.mPrevSelectedEvent = null + view.mFirstDayOfWeek = mFirstDayOfWeek if (view.mEvents.size() > 0) { - view.mSelectionAllday = mSelectionAllday; + view.mSelectionAllday = mSelectionAllday } else { - view.mSelectionAllday = false; + view.mSelectionAllday = false } // Redraw the screen so that the selection box will be redrawn. We may // have scrolled to a different part of the day in some other view // so the selection box in this view may no longer be visible. - view.recalc(); + view.recalc() } /** @@ -1379,11 +969,10 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * @param trackBallSelection true if the selection was made using the * trackball. */ - private void switchViews(boolean trackBallSelection) { - Event selectedEvent = mSelectedEvent; - - mPopup.dismiss(); - mLastPopupEventID = INVALID_EVENT_ID; + private fun switchViews(trackBallSelection: Boolean) { + val selectedEvent: Event? = mSelectedEvent + mPopup.dismiss() + mLastPopupEventID = INVALID_EVENT_ID if (mNumDays > 1) { // This is the Week view. // With touch, we always switch to Day/Agenda View @@ -1392,7 +981,7 @@ public class DayView extends View implements View.OnCreateContextMenuListener, if (trackBallSelection) { if (selectedEvent != null) { if (mIsAccessibilityEnabled) { - mAccessibilityMgr.interrupt(); + mAccessibilityMgr.interrupt() } } } @@ -1400,91 +989,89 @@ public class DayView extends View implements View.OnCreateContextMenuListener, } @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - mScrolling = false; - return super.onKeyUp(keyCode, event); + fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { + mScrolling = false + return super.onKeyUp(keyCode, event) } @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return super.onKeyDown(keyCode, event); + fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + return super.onKeyDown(keyCode, event) } - @Override - public boolean onHoverEvent(MotionEvent event) { - return true; + fun onHoverEvent(event: MotionEvent?): Boolean { + return true } - private boolean isTouchExplorationEnabled() { - return mIsAccessibilityEnabled && mAccessibilityMgr.isTouchExplorationEnabled(); - } + private val isTouchExplorationEnabled: Boolean + private get() = mIsAccessibilityEnabled && mAccessibilityMgr.isTouchExplorationEnabled() - private void sendAccessibilityEventAsNeeded(boolean speakEvents) { + private fun sendAccessibilityEventAsNeeded(speakEvents: Boolean) { if (!mIsAccessibilityEnabled) { - return; + return } - boolean dayChanged = mLastSelectionDayForAccessibility != mSelectionDayForAccessibility; - boolean hourChanged = mLastSelectionHourForAccessibility != mSelectionHourForAccessibility; - if (dayChanged || hourChanged || - mLastSelectedEventForAccessibility != mSelectedEventForAccessibility) { - mLastSelectionDayForAccessibility = mSelectionDayForAccessibility; - mLastSelectionHourForAccessibility = mSelectionHourForAccessibility; - mLastSelectedEventForAccessibility = mSelectedEventForAccessibility; - - StringBuilder b = new StringBuilder(); + val dayChanged = mLastSelectionDayForAccessibility != mSelectionDayForAccessibility + val hourChanged = mLastSelectionHourForAccessibility != mSelectionHourForAccessibility + if (dayChanged || hourChanged || mLastSelectedEventForAccessibility !== mSelectedEventForAccessibility) { + mLastSelectionDayForAccessibility = mSelectionDayForAccessibility + mLastSelectionHourForAccessibility = mSelectionHourForAccessibility + mLastSelectedEventForAccessibility = mSelectedEventForAccessibility + val b = StringBuilder() // Announce only the changes i.e. day or hour or both if (dayChanged) { - b.append(getSelectedTimeForAccessibility().format("%A ")); + b.append(selectedTimeForAccessibility.format("%A ")) } if (hourChanged) { - b.append(getSelectedTimeForAccessibility().format(mIs24HourFormat ? "%k" : "%l%p")); + b.append(selectedTimeForAccessibility.format(if (mIs24HourFormat) "%k" else "%l%p")) } if (dayChanged || hourChanged) { - b.append(PERIOD_SPACE); + b.append(PERIOD_SPACE) } - if (speakEvents) { if (mEventCountTemplate == null) { - mEventCountTemplate = mContext.getString(R.string.template_announce_item_index); + mEventCountTemplate = mContext.getString(R.string.template_announce_item_index) } // Read out the relevant event(s) - int numEvents = mSelectedEvents.size(); + val numEvents: Int = mSelectedEvents.size() if (numEvents > 0) { if (mSelectedEventForAccessibility == null) { // Read out all the events - int i = 1; - for (Event calEvent : mSelectedEvents) { + var i = 1 + for (calEvent in mSelectedEvents) { if (numEvents > 1) { // Read out x of numEvents if there are more than one event - mStringBuilder.setLength(0); - b.append(mFormatter.format(mEventCountTemplate, i++, numEvents)); - b.append(" "); + mStringBuilder.setLength(0) + b.append(mFormatter.format(mEventCountTemplate, i++, numEvents)) + b.append(" ") } - appendEventAccessibilityString(b, calEvent); + appendEventAccessibilityString(b, calEvent) } } else { if (numEvents > 1) { // Read out x of numEvents if there are more than one event - mStringBuilder.setLength(0); - b.append(mFormatter.format(mEventCountTemplate, mSelectedEvents - .indexOf(mSelectedEventForAccessibility) + 1, numEvents)); - b.append(" "); + mStringBuilder.setLength(0) + b.append( + mFormatter.format( + mEventCountTemplate, mSelectedEvents + .indexOf(mSelectedEventForAccessibility) + 1, numEvents + ) + ) + b.append(" ") } - appendEventAccessibilityString(b, mSelectedEventForAccessibility); + appendEventAccessibilityString(b, mSelectedEventForAccessibility) } } } - if (dayChanged || hourChanged || speakEvents) { - AccessibilityEvent event = AccessibilityEvent - .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED); - CharSequence msg = b.toString(); - event.getText().add(msg); - event.setAddedCount(msg.length()); - sendAccessibilityEventUnchecked(event); + val event: AccessibilityEvent = AccessibilityEvent + .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED) + val msg: CharSequence = b.toString() + event.getText().add(msg) + event.setAddedCount(msg.length()) + sendAccessibilityEventUnchecked(event) } } } @@ -1493,159 +1080,153 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * @param b * @param calEvent */ - private void appendEventAccessibilityString(StringBuilder b, Event calEvent) { - b.append(calEvent.getTitleAndLocation()); - b.append(PERIOD_SPACE); - String when; - int flags = DateUtils.FORMAT_SHOW_DATE; + private fun appendEventAccessibilityString(b: StringBuilder, calEvent: Event?) { + b.append(calEvent.getTitleAndLocation()) + b.append(PERIOD_SPACE) + val `when`: String + var flags: Int = DateUtils.FORMAT_SHOW_DATE if (calEvent.allDay) { - flags |= DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_WEEKDAY; + flags = flags or (DateUtils.FORMAT_UTC or DateUtils.FORMAT_SHOW_WEEKDAY) } else { - flags |= DateUtils.FORMAT_SHOW_TIME; + flags = flags or DateUtils.FORMAT_SHOW_TIME if (DateFormat.is24HourFormat(mContext)) { - flags |= DateUtils.FORMAT_24HOUR; + flags = flags or DateUtils.FORMAT_24HOUR } } - when = Utils.formatDateRange(mContext, calEvent.startMillis, calEvent.endMillis, flags); - b.append(when); - b.append(PERIOD_SPACE); + `when` = Utils.formatDateRange(mContext, calEvent.startMillis, calEvent.endMillis, flags) + b.append(`when`) + b.append(PERIOD_SPACE) } - private class GotoBroadcaster implements Animation.AnimationListener { - private final int mCounter; - private final Time mStart; - private final Time mEnd; - - public GotoBroadcaster(Time start, Time end) { - mCounter = ++sCounter; - mStart = start; - mEnd = end; - } - + private inner class GotoBroadcaster(start: Time, end: Time) : Animation.AnimationListener { + private val mCounter: Int + private val mStart: Time + private val mEnd: Time @Override - public void onAnimationEnd(Animation animation) { - DayView view = (DayView) mViewSwitcher.getCurrentView(); - view.mViewStartX = 0; - view = (DayView) mViewSwitcher.getNextView(); - view.mViewStartX = 0; - + fun onAnimationEnd(animation: Animation?) { + var view = mViewSwitcher.getCurrentView() as DayView + view.mViewStartX = 0 + view = mViewSwitcher.getNextView() + view.mViewStartX = 0 if (mCounter == sCounter) { - mController.sendEvent(this, EventType.GO_TO, mStart, mEnd, null, -1, - ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null); + mController.sendEvent( + this, EventType.GO_TO, mStart, mEnd, null, -1, + ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null + ) } } @Override - public void onAnimationRepeat(Animation animation) { + fun onAnimationRepeat(animation: Animation?) { } @Override - public void onAnimationStart(Animation animation) { + fun onAnimationStart(animation: Animation?) { + } + + init { + mCounter = ++sCounter + mStart = start + mEnd = end } } - private View switchViews(boolean forward, float xOffSet, float width, float velocity) { - mAnimationDistance = width - xOffSet; + private fun switchViews(forward: Boolean, xOffSet: Float, width: Float, velocity: Float): View { + mAnimationDistance = width - xOffSet if (DEBUG) { - Log.d(TAG, "switchViews(" + forward + ") O:" + xOffSet + " Dist:" + mAnimationDistance); + Log.d(TAG, "switchViews($forward) O:$xOffSet Dist:$mAnimationDistance") } - - float progress = Math.abs(xOffSet) / width; + var progress: Float = Math.abs(xOffSet) / width if (progress > 1.0f) { - progress = 1.0f; + progress = 1.0f } - - float inFromXValue, inToXValue; - float outFromXValue, outToXValue; + val inFromXValue: Float + val inToXValue: Float + val outFromXValue: Float + val outToXValue: Float if (forward) { - inFromXValue = 1.0f - progress; - inToXValue = 0.0f; - outFromXValue = -progress; - outToXValue = -1.0f; + inFromXValue = 1.0f - progress + inToXValue = 0.0f + outFromXValue = -progress + outToXValue = -1.0f } else { - inFromXValue = progress - 1.0f; - inToXValue = 0.0f; - outFromXValue = progress; - outToXValue = 1.0f; + inFromXValue = progress - 1.0f + inToXValue = 0.0f + outFromXValue = progress + outToXValue = 1.0f } - - final Time start = new Time(mBaseDate.timezone); - start.set(mController.getTime()); + val start = Time(mBaseDate.timezone) + start.set(mController.getTime()) if (forward) { - start.monthDay += mNumDays; + start.monthDay += mNumDays } else { - start.monthDay -= mNumDays; + start.monthDay -= mNumDays } - mController.setTime(start.normalize(true)); - - Time newSelected = start; - + mController.setTime(start.normalize(true)) + var newSelected: Time? = start if (mNumDays == 7) { - newSelected = new Time(start); - adjustToBeginningOfWeek(start); + newSelected = Time(start) + adjustToBeginningOfWeek(start) } - - final Time end = new Time(start); - end.monthDay += mNumDays - 1; + val end = Time(start) + end.monthDay += mNumDays - 1 // 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( - Animation.RELATIVE_TO_SELF, inFromXValue, - Animation.RELATIVE_TO_SELF, inToXValue, - Animation.ABSOLUTE, 0.0f, - Animation.ABSOLUTE, 0.0f); - - TranslateAnimation outAnimation = new TranslateAnimation( - Animation.RELATIVE_TO_SELF, outFromXValue, - Animation.RELATIVE_TO_SELF, outToXValue, - Animation.ABSOLUTE, 0.0f, - Animation.ABSOLUTE, 0.0f); - - long duration = calculateDuration(width - Math.abs(xOffSet), width, velocity); - inAnimation.setDuration(duration); - inAnimation.setInterpolator(mHScrollInterpolator); - outAnimation.setInterpolator(mHScrollInterpolator); - outAnimation.setDuration(duration); - outAnimation.setAnimationListener(new GotoBroadcaster(start, end)); - mViewSwitcher.setInAnimation(inAnimation); - mViewSwitcher.setOutAnimation(outAnimation); - - DayView view = (DayView) mViewSwitcher.getCurrentView(); - view.cleanup(); - mViewSwitcher.showNext(); - view = (DayView) mViewSwitcher.getCurrentView(); - view.setSelected(newSelected, true, false); - view.requestFocus(); - view.reloadEvents(); - view.updateTitle(); - view.restartCurrentTimeUpdates(); - - return view; + val inAnimation = TranslateAnimation( + Animation.RELATIVE_TO_SELF, inFromXValue, + Animation.RELATIVE_TO_SELF, inToXValue, + Animation.ABSOLUTE, 0.0f, + Animation.ABSOLUTE, 0.0f + ) + val outAnimation = TranslateAnimation( + Animation.RELATIVE_TO_SELF, outFromXValue, + Animation.RELATIVE_TO_SELF, outToXValue, + Animation.ABSOLUTE, 0.0f, + Animation.ABSOLUTE, 0.0f + ) + val duration = calculateDuration(width - Math.abs(xOffSet), width, velocity) + inAnimation.setDuration(duration) + inAnimation.setInterpolator(mHScrollInterpolator) + outAnimation.setInterpolator(mHScrollInterpolator) + outAnimation.setDuration(duration) + outAnimation.setAnimationListener(GotoBroadcaster(start, end)) + mViewSwitcher.setInAnimation(inAnimation) + mViewSwitcher.setOutAnimation(outAnimation) + var view = mViewSwitcher.getCurrentView() as DayView + view.cleanup() + mViewSwitcher.showNext() + view = mViewSwitcher.getCurrentView() + view.setSelected(newSelected, true, false) + view.requestFocus() + view.reloadEvents() + view.updateTitle() + view.restartCurrentTimeUpdates() + return view } // This is called after scrolling stops to move the selected hour // to the visible part of the screen. - private void resetSelectedHour() { + private fun resetSelectedHour() { if (mSelectionHour < mFirstHour + 1) { - setSelectedHour(mFirstHour + 1); - setSelectedEvent(null); - mSelectedEvents.clear(); - mComputeSelectedEvents = true; + setSelectedHour(mFirstHour + 1) + setSelectedEvent(null) + mSelectedEvents.clear() + mComputeSelectedEvents = true } else if (mSelectionHour > mFirstHour + mNumHours - 3) { - setSelectedHour(mFirstHour + mNumHours - 3); - setSelectedEvent(null); - mSelectedEvents.clear(); - mComputeSelectedEvents = true; + setSelectedHour(mFirstHour + mNumHours - 3) + setSelectedEvent(null) + mSelectedEvents.clear() + mComputeSelectedEvents = true } } - private void initFirstHour() { - mFirstHour = mSelectionHour - mNumHours / 5; + private fun initFirstHour() { + mFirstHour = mSelectionHour - mNumHours / 5 if (mFirstHour < 0) { - mFirstHour = 0; + mFirstHour = 0 } else if (mFirstHour + mNumHours > 24) { - mFirstHour = 24 - mNumHours; + mFirstHour = 24 - mNumHours } } @@ -1653,23 +1234,22 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * Recomputes the first full hour that is visible on screen after the * screen is scrolled. */ - private void computeFirstHour() { + private fun computeFirstHour() { // Compute the first full hour that is visible on screen - mFirstHour = (mViewStartY + mCellHeight + HOUR_GAP - 1) / (mCellHeight + HOUR_GAP); - mFirstHourOffset = mFirstHour * (mCellHeight + HOUR_GAP) - mViewStartY; + mFirstHour = (mViewStartY + mCellHeight + HOUR_GAP - 1) / (mCellHeight + HOUR_GAP) + mFirstHourOffset = mFirstHour * (mCellHeight + HOUR_GAP) - mViewStartY } - private void adjustHourSelection() { + private fun adjustHourSelection() { if (mSelectionHour < 0) { - setSelectedHour(0); + setSelectedHour(0) if (mMaxAlldayEvents > 0) { - mPrevSelectedEvent = null; - mSelectionAllday = true; + mPrevSelectedEvent = null + mSelectionAllday = true } } - if (mSelectionHour > 23) { - setSelectedHour(23); + setSelectedHour(23) } // If the selected hour is at least 2 time slots from the top and @@ -1682,52 +1262,48 @@ public class DayView extends View implements View.OnCreateContextMenuListener, // before jumping to the all-day event area. // Exception 2: if 12am is on screen, then allow the user to select // 12am before going up to the all-day event area. - int daynum = mSelectionDay - mFirstJulianDay; - if (daynum < mEarliestStartHour.length && daynum >= 0 - && mMaxAlldayEvents > 0 - && mEarliestStartHour[daynum] > mSelectionHour - && mFirstHour > 0 && mFirstHour < 8) { - mPrevSelectedEvent = null; - mSelectionAllday = true; - setSelectedHour(mFirstHour + 1); - return; + val daynum = mSelectionDay - mFirstJulianDay + if (daynum < mEarliestStartHour.size && daynum >= 0 && mMaxAlldayEvents > 0 && mEarliestStartHour[daynum] > mSelectionHour && mFirstHour > 0 && mFirstHour < 8) { + mPrevSelectedEvent = null + mSelectionAllday = true + setSelectedHour(mFirstHour + 1) + return } - if (mFirstHour > 0) { - mFirstHour -= 1; - mViewStartY -= (mCellHeight + HOUR_GAP); + mFirstHour -= 1 + mViewStartY -= mCellHeight + HOUR_GAP if (mViewStartY < 0) { - mViewStartY = 0; + mViewStartY = 0 } - return; + return } } - if (mSelectionHour > mFirstHour + mNumHours - 3) { if (mFirstHour < 24 - mNumHours) { - mFirstHour += 1; - mViewStartY += (mCellHeight + HOUR_GAP); + mFirstHour += 1 + mViewStartY += mCellHeight + HOUR_GAP if (mViewStartY > mMaxViewStartY) { - mViewStartY = mMaxViewStartY; + mViewStartY = mMaxViewStartY } - return; + return } else if (mFirstHour == 24 - mNumHours && mFirstHourOffset > 0) { - mViewStartY = mMaxViewStartY; + mViewStartY = mMaxViewStartY } } } - void clearCachedEvents() { - mLastReloadMillis = 0; + fun clearCachedEvents() { + mLastReloadMillis = 0 } - private final Runnable mCancelCallback = new Runnable() { - public void run() { - clearCachedEvents(); + private val mCancelCallback: Runnable = object : Runnable() { + fun run() { + clearCachedEvents() } - }; + } - /* package */ void reloadEvents() { + /* package */ + fun reloadEvents() { // Protect against this being called before this view has been // initialized. // if (mContext == null) { @@ -1735,99 +1311,92 @@ public class DayView extends View implements View.OnCreateContextMenuListener, // } // Make sure our time zones are up to date - mTZUpdater.run(); - - setSelectedEvent(null); - mPrevSelectedEvent = null; - mSelectedEvents.clear(); + mTZUpdater.run() + setSelectedEvent(null) + mPrevSelectedEvent = null + mSelectedEvents.clear() // The start date is the beginning of the week at 12am - Time weekStart = new Time(Utils.getTimeZone(mContext, mTZUpdater)); - weekStart.set(mBaseDate); - weekStart.hour = 0; - weekStart.minute = 0; - weekStart.second = 0; - long millis = weekStart.normalize(true /* ignore isDst */); + val weekStart = Time(Utils.getTimeZone(mContext, mTZUpdater)) + weekStart.set(mBaseDate) + weekStart.hour = 0 + weekStart.minute = 0 + weekStart.second = 0 + val millis: Long = weekStart.normalize(true /* ignore isDst */) // Avoid reloading events unnecessarily. if (millis == mLastReloadMillis) { - return; + return } - mLastReloadMillis = millis; + mLastReloadMillis = millis // load events in the background // mContext.startProgressSpinner(); - final ArrayList events = new ArrayList(); - mEventLoader.loadEventsInBackground(mNumDays, events, mFirstJulianDay, new Runnable() { - - public void run() { - boolean fadeinEvents = mFirstJulianDay != mLoadedFirstJulianDay; - mEvents = events; - mLoadedFirstJulianDay = mFirstJulianDay; + val events: ArrayList = ArrayList() + mEventLoader.loadEventsInBackground(mNumDays, events, mFirstJulianDay, object : Runnable() { + fun run() { + val fadeinEvents = mFirstJulianDay != mLoadedFirstJulianDay + mEvents = events + mLoadedFirstJulianDay = mFirstJulianDay if (mAllDayEvents == null) { - mAllDayEvents = new ArrayList(); + mAllDayEvents = ArrayList() } else { - mAllDayEvents.clear(); + mAllDayEvents.clear() } // Create a shorter array for all day events - for (Event e : events) { + for (e in events) { if (e.drawAsAllday()) { - mAllDayEvents.add(e); + mAllDayEvents.add(e) } } // New events, new layouts - if (mLayouts == null || mLayouts.length < events.size()) { - mLayouts = new StaticLayout[events.size()]; + if (mLayouts == null || mLayouts!!.size < events.size()) { + mLayouts = arrayOfNulls(events.size()) } else { - Arrays.fill(mLayouts, null); + Arrays.fill(mLayouts, null) } - - if (mAllDayLayouts == null || mAllDayLayouts.length < mAllDayEvents.size()) { - mAllDayLayouts = new StaticLayout[events.size()]; + if (mAllDayLayouts == null || mAllDayLayouts!!.size < mAllDayEvents.size()) { + mAllDayLayouts = arrayOfNulls(events.size()) } else { - Arrays.fill(mAllDayLayouts, null); + Arrays.fill(mAllDayLayouts, null) } - - computeEventRelations(); - - mRemeasure = true; - mComputeSelectedEvents = true; - recalc(); + computeEventRelations() + mRemeasure = true + mComputeSelectedEvents = true + recalc() // Start animation to cross fade the events if (fadeinEvents) { if (mEventsCrossFadeAnimation == null) { mEventsCrossFadeAnimation = - ObjectAnimator.ofInt(DayView.this, "EventsAlpha", 0, 255); - mEventsCrossFadeAnimation.setDuration(EVENTS_CROSS_FADE_DURATION); + ObjectAnimator.ofInt(this@DayView, "EventsAlpha", 0, 255) + mEventsCrossFadeAnimation.setDuration(EVENTS_CROSS_FADE_DURATION) } - mEventsCrossFadeAnimation.start(); - } else{ - invalidate(); + mEventsCrossFadeAnimation.start() + } else { + invalidate() } } - }, mCancelCallback); + }, mCancelCallback) } - public void setEventsAlpha(int alpha) { - mEventsAlpha = alpha; - invalidate(); - } - - public int getEventsAlpha() { - return mEventsAlpha; - } + var eventsAlpha: Int + get() = mEventsAlpha + set(alpha) { + mEventsAlpha = alpha + invalidate() + } - public void stopEventsAnimation() { + fun stopEventsAnimation() { if (mEventsCrossFadeAnimation != null) { - mEventsCrossFadeAnimation.cancel(); + mEventsCrossFadeAnimation.cancel() } - mEventsAlpha = 255; + mEventsAlpha = 255 } - private void computeEventRelations() { + private fun computeEventRelations() { // Compute the layout relation between each event before measuring cell // width, as the cell width should be adjusted along with the relation. // @@ -1840,235 +1409,227 @@ public class DayView extends View implements View.OnCreateContextMenuListener, // Make a pass over all the events, and keep track of the maximum // number of all-day events in any one day. Also, keep track of // the earliest event in each day. - int maxAllDayEvents = 0; - final ArrayList events = mEvents; - final int len = events.size(); + var maxAllDayEvents = 0 + val events: ArrayList = mEvents + val len: Int = events.size() // Num of all-day-events on each day. - final int eventsCount[] = new int[mLastJulianDay - mFirstJulianDay + 1]; - Arrays.fill(eventsCount, 0); - for (int ii = 0; ii < len; ii++) { - Event event = events.get(ii); + val eventsCount = IntArray(mLastJulianDay - mFirstJulianDay + 1) + Arrays.fill(eventsCount, 0) + for (ii in 0 until len) { + val event: Event = events.get(ii) if (event.startDay > mLastJulianDay || event.endDay < mFirstJulianDay) { - continue; + continue } if (event.drawAsAllday()) { // Count all the events being drawn as allDay events - final int firstDay = Math.max(event.startDay, mFirstJulianDay); - final int lastDay = Math.min(event.endDay, mLastJulianDay); - for (int day = firstDay; day <= lastDay; day++) { - final int count = ++eventsCount[day - mFirstJulianDay]; + val firstDay: Int = Math.max(event.startDay, mFirstJulianDay) + val lastDay: Int = Math.min(event.endDay, mLastJulianDay) + for (day in firstDay..lastDay) { + val count = ++eventsCount[day - mFirstJulianDay] if (maxAllDayEvents < count) { - maxAllDayEvents = count; + maxAllDayEvents = count } } - - int daynum = event.startDay - mFirstJulianDay; - int durationDays = event.endDay - event.startDay + 1; + var daynum: Int = event.startDay - mFirstJulianDay + var durationDays: Int = event.endDay - event.startDay + 1 if (daynum < 0) { - durationDays += daynum; - daynum = 0; + durationDays += daynum + daynum = 0 } if (daynum + durationDays > mNumDays) { - durationDays = mNumDays - daynum; + durationDays = mNumDays - daynum } - for (int day = daynum; durationDays > 0; day++, durationDays--) { - mHasAllDayEvent[day] = true; + var day = daynum + while (durationDays > 0) { + mHasAllDayEvent[day] = true + day++ + durationDays-- } } else { - int daynum = event.startDay - mFirstJulianDay; - int hour = event.startTime / 60; + var daynum: Int = event.startDay - mFirstJulianDay + var hour: Int = event.startTime / 60 if (daynum >= 0 && hour < mEarliestStartHour[daynum]) { - mEarliestStartHour[daynum] = hour; + mEarliestStartHour[daynum] = hour } // Also check the end hour in case the event spans more than // one day. - daynum = event.endDay - mFirstJulianDay; - hour = event.endTime / 60; + daynum = event.endDay - mFirstJulianDay + hour = event.endTime / 60 if (daynum < mNumDays && hour < mEarliestStartHour[daynum]) { - mEarliestStartHour[daynum] = hour; + mEarliestStartHour[daynum] = hour } } } - mMaxAlldayEvents = maxAllDayEvents; - initAllDayHeights(); + mMaxAlldayEvents = maxAllDayEvents + initAllDayHeights() } @Override - protected void onDraw(Canvas canvas) { + protected fun onDraw(canvas: Canvas) { if (mRemeasure) { - remeasure(getWidth(), getHeight()); - mRemeasure = false; + remeasure(getWidth(), getHeight()) + mRemeasure = false } - canvas.save(); - - float yTranslate = -mViewStartY + DAY_HEADER_HEIGHT + mAlldayHeight; + canvas.save() + val yTranslate = (-mViewStartY + DAY_HEADER_HEIGHT + mAlldayHeight).toFloat() // offset canvas by the current drag and header position - canvas.translate(-mViewStartX, yTranslate); + canvas.translate(-mViewStartX, yTranslate) // clip to everything below the allDay area - Rect dest = mDestRect; - dest.top = (int) (mFirstCell - yTranslate); - dest.bottom = (int) (mViewHeight - yTranslate); - dest.left = 0; - dest.right = mViewWidth; - canvas.save(); - canvas.clipRect(dest); + val dest: Rect = mDestRect + dest.top = (mFirstCell - yTranslate).toInt() + dest.bottom = (mViewHeight - yTranslate).toInt() + dest.left = 0 + dest.right = mViewWidth + canvas.save() + canvas.clipRect(dest) // Draw the movable part of the view - doDraw(canvas); + doDraw(canvas) // restore to having no clip - canvas.restore(); - - if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { - float xTranslate; - if (mViewStartX > 0) { - xTranslate = mViewWidth; + canvas.restore() + if (mTouchMode and TOUCH_MODE_HSCROLL != 0) { + val xTranslate: Float + xTranslate = if (mViewStartX > 0) { + mViewWidth.toFloat() } else { - xTranslate = -mViewWidth; + -mViewWidth.toFloat() } // Move the canvas around to prep it for the next view // specifically, shift it by a screen and undo the // yTranslation which will be redone in the nextView's onDraw(). - canvas.translate(xTranslate, -yTranslate); - DayView nextView = (DayView) mViewSwitcher.getNextView(); + canvas.translate(xTranslate, -yTranslate) + val nextView = mViewSwitcher.getNextView() as DayView // Prevent infinite recursive calls to onDraw(). - nextView.mTouchMode = TOUCH_MODE_INITIAL_STATE; - - nextView.onDraw(canvas); + nextView.mTouchMode = TOUCH_MODE_INITIAL_STATE + nextView.onDraw(canvas) // Move it back for this view - canvas.translate(-xTranslate, 0); + canvas.translate(-xTranslate, 0) } else { // If we drew another view we already translated it back // If we didn't draw another view we should be at the edge of the // screen - canvas.translate(mViewStartX, -yTranslate); + canvas.translate(mViewStartX, -yTranslate) } // Draw the fixed areas (that don't scroll) directly to the canvas. - drawAfterScroll(canvas); + drawAfterScroll(canvas) if (mComputeSelectedEvents && mUpdateToast) { - mUpdateToast = false; + mUpdateToast = false } - mComputeSelectedEvents = false; + mComputeSelectedEvents = false // Draw overscroll glow if (!mEdgeEffectTop.isFinished()) { if (DAY_HEADER_HEIGHT != 0) { - canvas.translate(0, DAY_HEADER_HEIGHT); + canvas.translate(0, DAY_HEADER_HEIGHT) } if (mEdgeEffectTop.draw(canvas)) { - invalidate(); + invalidate() } if (DAY_HEADER_HEIGHT != 0) { - canvas.translate(0, -DAY_HEADER_HEIGHT); + canvas.translate(0, -DAY_HEADER_HEIGHT) } } if (!mEdgeEffectBottom.isFinished()) { - canvas.rotate(180, mViewWidth/2, mViewHeight/2); + canvas.rotate(180, mViewWidth / 2, mViewHeight / 2) if (mEdgeEffectBottom.draw(canvas)) { - invalidate(); + invalidate() } } - canvas.restore(); + canvas.restore() } - private void drawAfterScroll(Canvas canvas) { - Paint p = mPaint; - Rect r = mRect; - - drawAllDayHighlights(r, canvas, p); + private fun drawAfterScroll(canvas: Canvas) { + val p: Paint = mPaint + val r: Rect = mRect + drawAllDayHighlights(r, canvas, p) if (mMaxAlldayEvents != 0) { - drawAllDayEvents(mFirstJulianDay, mNumDays, canvas, p); - drawUpperLeftCorner(r, canvas, p); + drawAllDayEvents(mFirstJulianDay, mNumDays, canvas, p) + drawUpperLeftCorner(r, canvas, p) } - - drawScrollLine(r, canvas, p); - drawDayHeaderLoop(r, canvas, p); + drawScrollLine(r, canvas, p) + drawDayHeaderLoop(r, canvas, p) // Draw the AM and PM indicators if we're in 12 hour mode if (!mIs24HourFormat) { - drawAmPm(canvas, p); + drawAmPm(canvas, p) } } // This isn't really the upper-left corner. It's the square area just // below the upper-left corner, above the hours and to the left of the // all-day area. - private void drawUpperLeftCorner(Rect r, Canvas canvas, Paint p) { - setupHourTextPaint(p); + private fun drawUpperLeftCorner(r: Rect, canvas: Canvas, p: Paint) { + setupHourTextPaint(p) if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { // Draw the allDay expand/collapse icon if (mUseExpandIcon) { - mExpandAlldayDrawable.setBounds(mExpandAllDayRect); - mExpandAlldayDrawable.draw(canvas); + mExpandAlldayDrawable.setBounds(mExpandAllDayRect) + mExpandAlldayDrawable.draw(canvas) } else { - mCollapseAlldayDrawable.setBounds(mExpandAllDayRect); - mCollapseAlldayDrawable.draw(canvas); + mCollapseAlldayDrawable.setBounds(mExpandAllDayRect) + mCollapseAlldayDrawable.draw(canvas) } } } - private void drawScrollLine(Rect r, Canvas canvas, Paint p) { - final int right = computeDayLeftPosition(mNumDays); - final int y = mFirstCell - 1; - - p.setAntiAlias(false); - p.setStyle(Style.FILL); - - p.setColor(mCalendarGridLineInnerHorizontalColor); - p.setStrokeWidth(GRID_LINE_INNER_WIDTH); - canvas.drawLine(GRID_LINE_LEFT_MARGIN, y, right, y, p); - p.setAntiAlias(true); + private fun drawScrollLine(r: Rect, canvas: Canvas, p: Paint) { + val right = computeDayLeftPosition(mNumDays) + val y = mFirstCell - 1 + p.setAntiAlias(false) + p.setStyle(Style.FILL) + p.setColor(mCalendarGridLineInnerHorizontalColor) + p.setStrokeWidth(GRID_LINE_INNER_WIDTH) + canvas.drawLine(GRID_LINE_LEFT_MARGIN, y, right, y, p) + p.setAntiAlias(true) } // Computes the x position for the left side of the given day (base 0) - private int computeDayLeftPosition(int day) { - int effectiveWidth = mViewWidth - mHoursWidth; - return day * effectiveWidth / mNumDays + mHoursWidth; + private fun computeDayLeftPosition(day: Int): Int { + val effectiveWidth = mViewWidth - mHoursWidth + return day * effectiveWidth / mNumDays + mHoursWidth } - private void drawAllDayHighlights(Rect r, Canvas canvas, Paint p) { + private fun drawAllDayHighlights(r: Rect, canvas: Canvas, p: Paint) { if (mFutureBgColor != 0) { // First, color the labels area light gray - r.top = 0; - r.bottom = DAY_HEADER_HEIGHT; - r.left = 0; - r.right = mViewWidth; - p.setColor(mBgColor); - p.setStyle(Style.FILL); - canvas.drawRect(r, p); + r.top = 0 + r.bottom = DAY_HEADER_HEIGHT + r.left = 0 + r.right = mViewWidth + p.setColor(mBgColor) + p.setStyle(Style.FILL) + canvas.drawRect(r, p) // and the area that says All day - r.top = DAY_HEADER_HEIGHT; - r.bottom = mFirstCell - 1; - r.left = 0; - r.right = mHoursWidth; - canvas.drawRect(r, p); - - int startIndex = -1; - - int todayIndex = mTodayJulianDay - mFirstJulianDay; + r.top = DAY_HEADER_HEIGHT + r.bottom = mFirstCell - 1 + r.left = 0 + r.right = mHoursWidth + canvas.drawRect(r, p) + var startIndex = -1 + val todayIndex = mTodayJulianDay - mFirstJulianDay if (todayIndex < 0) { // Future - startIndex = 0; + startIndex = 0 } else if (todayIndex >= 1 && todayIndex + 1 < mNumDays) { // Multiday - tomorrow is visible. - startIndex = todayIndex + 1; + startIndex = todayIndex + 1 } - if (startIndex >= 0) { // Draw the future highlight - r.top = 0; - r.bottom = mFirstCell - 1; - r.left = computeDayLeftPosition(startIndex) + 1; - r.right = computeDayLeftPosition(mNumDays); - p.setColor(mFutureBgColor); - p.setStyle(Style.FILL); - canvas.drawRect(r, p); + r.top = 0 + r.bottom = mFirstCell - 1 + r.left = computeDayLeftPosition(startIndex) + 1 + r.right = computeDayLeftPosition(mNumDays) + p.setColor(mFutureBgColor) + p.setStyle(Style.FILL) + canvas.drawRect(r, p) } } } - private void drawDayHeaderLoop(Rect r, Canvas canvas, Paint p) { + private fun drawDayHeaderLoop(r: Rect, canvas: Canvas, p: Paint) { // Draw the horizontal day background banner // p.setColor(mCalendarDateBannerBackground); // r.top = 0; @@ -2083,231 +1644,226 @@ public class DayView extends View implements View.OnCreateContextMenuListener, // p.setColor(mCalendarGridAreaBackground); // canvas.drawRect(r, p); if (mNumDays == 1 && ONE_DAY_HEADER_HEIGHT == 0) { - return; - } - - p.setTypeface(mBold); - p.setTextAlign(Paint.Align.RIGHT); - int cell = mFirstJulianDay; - - String[] dayNames; - if (mDateStrWidth < mCellWidth) { - dayNames = mDayStrs; + return + } + p.setTypeface(mBold) + p.setTextAlign(Paint.Align.RIGHT) + var cell = mFirstJulianDay + val dayNames: Array + dayNames = if (mDateStrWidth < mCellWidth) { + mDayStrs } else { - dayNames = mDayStrs2Letter; + mDayStrs2Letter } - - p.setAntiAlias(true); - for (int day = 0; day < mNumDays; day++, cell++) { - int dayOfWeek = day + mFirstVisibleDayOfWeek; + p.setAntiAlias(true) + var day = 0 + while (day < mNumDays) { + var dayOfWeek = day + mFirstVisibleDayOfWeek if (dayOfWeek >= 14) { - dayOfWeek -= 14; + dayOfWeek -= 14 } - - int color = mCalendarDateBannerTextColor; + var color = mCalendarDateBannerTextColor if (mNumDays == 1) { if (dayOfWeek == Time.SATURDAY) { - color = mWeek_saturdayColor; + color = mWeek_saturdayColor } else if (dayOfWeek == Time.SUNDAY) { - color = mWeek_sundayColor; + color = mWeek_sundayColor } } else { - final int column = day % 7; + val column = day % 7 if (Utils.isSaturday(column, mFirstDayOfWeek)) { - color = mWeek_saturdayColor; + color = mWeek_saturdayColor } else if (Utils.isSunday(column, mFirstDayOfWeek)) { - color = mWeek_sundayColor; + color = mWeek_sundayColor } } - - p.setColor(color); - drawDayHeader(dayNames[dayOfWeek], day, cell, canvas, p); + p.setColor(color) + drawDayHeader(dayNames[dayOfWeek], day, cell, canvas, p) + day++ + cell++ } - p.setTypeface(null); + p.setTypeface(null) } - private void drawAmPm(Canvas canvas, Paint p) { - p.setColor(mCalendarAmPmLabel); - p.setTextSize(AMPM_TEXT_SIZE); - p.setTypeface(mBold); - p.setAntiAlias(true); - p.setTextAlign(Paint.Align.RIGHT); - String text = mAmString; + private fun drawAmPm(canvas: Canvas, p: Paint) { + p.setColor(mCalendarAmPmLabel) + p.setTextSize(AMPM_TEXT_SIZE) + p.setTypeface(mBold) + p.setAntiAlias(true) + p.setTextAlign(Paint.Align.RIGHT) + var text = mAmString if (mFirstHour >= 12) { - text = mPmString; + text = mPmString } - int y = mFirstCell + mFirstHourOffset + 2 * mHoursTextHeight + HOUR_GAP; - canvas.drawText(text, HOURS_LEFT_MARGIN, y, p); - + var y = mFirstCell + mFirstHourOffset + 2 * mHoursTextHeight + HOUR_GAP + canvas.drawText(text, HOURS_LEFT_MARGIN, y, p) if (mFirstHour < 12 && mFirstHour + mNumHours > 12) { // Also draw the "PM" - text = mPmString; - y = mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP) - + 2 * mHoursTextHeight + HOUR_GAP; - canvas.drawText(text, HOURS_LEFT_MARGIN, y, p); - } - } - - private void drawCurrentTimeLine(Rect r, final int day, final int top, Canvas canvas, - Paint p) { - r.left = computeDayLeftPosition(day) - CURRENT_TIME_LINE_SIDE_BUFFER + 1; - r.right = computeDayLeftPosition(day + 1) + CURRENT_TIME_LINE_SIDE_BUFFER + 1; - - r.top = top - CURRENT_TIME_LINE_TOP_OFFSET; - r.bottom = r.top + mCurrentTimeLine.getIntrinsicHeight(); - - mCurrentTimeLine.setBounds(r); - mCurrentTimeLine.draw(canvas); + text = mPmString + y = + mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP) + 2 * mHoursTextHeight + HOUR_GAP + canvas.drawText(text, HOURS_LEFT_MARGIN, y, p) + } + } + + private fun drawCurrentTimeLine( + r: Rect, day: Int, top: Int, canvas: Canvas, + p: Paint + ) { + r.left = computeDayLeftPosition(day) - CURRENT_TIME_LINE_SIDE_BUFFER + 1 + r.right = computeDayLeftPosition(day + 1) + CURRENT_TIME_LINE_SIDE_BUFFER + 1 + r.top = top - CURRENT_TIME_LINE_TOP_OFFSET + r.bottom = r.top + mCurrentTimeLine.getIntrinsicHeight() + mCurrentTimeLine.setBounds(r) + mCurrentTimeLine.draw(canvas) if (mAnimateToday) { - mCurrentTimeAnimateLine.setBounds(r); - mCurrentTimeAnimateLine.setAlpha(mAnimateTodayAlpha); - mCurrentTimeAnimateLine.draw(canvas); + mCurrentTimeAnimateLine.setBounds(r) + mCurrentTimeAnimateLine.setAlpha(mAnimateTodayAlpha) + mCurrentTimeAnimateLine.draw(canvas) } } - private void doDraw(Canvas canvas) { - Paint p = mPaint; - Rect r = mRect; - + private fun doDraw(canvas: Canvas) { + val p: Paint = mPaint + val r: Rect = mRect if (mFutureBgColor != 0) { - drawBgColors(r, canvas, p); + drawBgColors(r, canvas, p) } - drawGridBackground(r, canvas, p); - drawHours(r, canvas, p); + drawGridBackground(r, canvas, p) + drawHours(r, canvas, p) // Draw each day - int cell = mFirstJulianDay; - p.setAntiAlias(false); - int alpha = p.getAlpha(); - p.setAlpha(mEventsAlpha); - for (int day = 0; day < mNumDays; day++, cell++) { + var cell = mFirstJulianDay + p.setAntiAlias(false) + val alpha: Int = p.getAlpha() + p.setAlpha(mEventsAlpha) + var day = 0 + while (day < mNumDays) { + // TODO Wow, this needs cleanup. drawEvents loop through all the // events on every call. - drawEvents(cell, day, HOUR_GAP, canvas, p); + drawEvents(cell, day, HOUR_GAP, canvas, p) // If this is today if (cell == mTodayJulianDay) { - int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP) - + ((mCurrentTime.minute * mCellHeight) / 60) + 1; + val lineY: Int = + mCurrentTime.hour * (mCellHeight + HOUR_GAP) + mCurrentTime.minute * mCellHeight / 60 + 1 // And the current time shows up somewhere on the screen if (lineY >= mViewStartY && lineY < mViewStartY + mViewHeight - 2) { - drawCurrentTimeLine(r, day, lineY, canvas, p); + drawCurrentTimeLine(r, day, lineY, canvas, p) } } + day++ + cell++ } - p.setAntiAlias(true); - p.setAlpha(alpha); + p.setAntiAlias(true) + p.setAlpha(alpha) } - private void drawHours(Rect r, Canvas canvas, Paint p) { - setupHourTextPaint(p); - - int y = HOUR_GAP + mHoursTextHeight + HOURS_TOP_MARGIN; - - for (int i = 0; i < 24; i++) { - String time = mHourStrs[i]; - canvas.drawText(time, HOURS_LEFT_MARGIN, y, p); - y += mCellHeight + HOUR_GAP; + private fun drawHours(r: Rect, canvas: Canvas, p: Paint) { + setupHourTextPaint(p) + var y = HOUR_GAP + mHoursTextHeight + HOURS_TOP_MARGIN + for (i in 0..23) { + val time = mHourStrs[i] + canvas.drawText(time, HOURS_LEFT_MARGIN, y, p) + y += mCellHeight + HOUR_GAP } } - private void setupHourTextPaint(Paint p) { - p.setColor(mCalendarHourLabelColor); - p.setTextSize(HOURS_TEXT_SIZE); - p.setTypeface(Typeface.DEFAULT); - p.setTextAlign(Paint.Align.RIGHT); - p.setAntiAlias(true); + private fun setupHourTextPaint(p: Paint) { + p.setColor(mCalendarHourLabelColor) + p.setTextSize(HOURS_TEXT_SIZE) + p.setTypeface(Typeface.DEFAULT) + p.setTextAlign(Paint.Align.RIGHT) + p.setAntiAlias(true) } - private void drawDayHeader(String dayStr, int day, int cell, Canvas canvas, Paint p) { - int dateNum = mFirstVisibleDate + day; - int x; + private fun drawDayHeader(dayStr: String?, day: Int, cell: Int, canvas: Canvas, p: Paint) { + var dateNum = mFirstVisibleDate + day + val x: Int if (dateNum > mMonthLength) { - dateNum -= mMonthLength; + dateNum -= mMonthLength } - p.setAntiAlias(true); - - int todayIndex = mTodayJulianDay - mFirstJulianDay; + p.setAntiAlias(true) + val todayIndex = mTodayJulianDay - mFirstJulianDay // Draw day of the month - String dateNumStr = String.valueOf(dateNum); + val dateNumStr: String = String.valueOf(dateNum) if (mNumDays > 1) { - float y = DAY_HEADER_HEIGHT - DAY_HEADER_BOTTOM_MARGIN; + val y = (DAY_HEADER_HEIGHT - DAY_HEADER_BOTTOM_MARGIN).toFloat() // Draw day of the month - x = computeDayLeftPosition(day + 1) - DAY_HEADER_RIGHT_MARGIN; - p.setTextAlign(Align.RIGHT); - p.setTextSize(DATE_HEADER_FONT_SIZE); - - p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT); - canvas.drawText(dateNumStr, x, y, p); + x = computeDayLeftPosition(day + 1) - DAY_HEADER_RIGHT_MARGIN + p.setTextAlign(Align.RIGHT) + p.setTextSize(DATE_HEADER_FONT_SIZE) + p.setTypeface(if (todayIndex == day) mBold else Typeface.DEFAULT) + canvas.drawText(dateNumStr, x, y, p) // Draw day of the week - x -= p.measureText(" " + dateNumStr); - p.setTextSize(DAY_HEADER_FONT_SIZE); - p.setTypeface(Typeface.DEFAULT); - canvas.drawText(dayStr, x, y, p); + x -= p.measureText(" $dateNumStr") + p.setTextSize(DAY_HEADER_FONT_SIZE) + p.setTypeface(Typeface.DEFAULT) + canvas.drawText(dayStr, x, y, p) } else { - float y = ONE_DAY_HEADER_HEIGHT - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN; - p.setTextAlign(Align.LEFT); + val y = (ONE_DAY_HEADER_HEIGHT - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN).toFloat() + p.setTextAlign(Align.LEFT) // Draw day of the week - x = computeDayLeftPosition(day) + DAY_HEADER_ONE_DAY_LEFT_MARGIN; - p.setTextSize(DAY_HEADER_FONT_SIZE); - p.setTypeface(Typeface.DEFAULT); - canvas.drawText(dayStr, x, y, p); + x = computeDayLeftPosition(day) + DAY_HEADER_ONE_DAY_LEFT_MARGIN + p.setTextSize(DAY_HEADER_FONT_SIZE) + p.setTypeface(Typeface.DEFAULT) + canvas.drawText(dayStr, x, y, p) // Draw day of the month - x += p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN; - p.setTextSize(DATE_HEADER_FONT_SIZE); - p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT); - canvas.drawText(dateNumStr, x, y, p); + x += p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN + p.setTextSize(DATE_HEADER_FONT_SIZE) + p.setTypeface(if (todayIndex == day) mBold else Typeface.DEFAULT) + canvas.drawText(dateNumStr, x, y, p) } } - private void drawGridBackground(Rect r, Canvas canvas, Paint p) { - Paint.Style savedStyle = p.getStyle(); - - final float stopX = computeDayLeftPosition(mNumDays); - float y = 0; - final float deltaY = mCellHeight + HOUR_GAP; - int linesIndex = 0; - final float startY = 0; - final float stopY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP); - float x = mHoursWidth; + private fun drawGridBackground(r: Rect, canvas: Canvas, p: Paint) { + val savedStyle: Style = p.getStyle() + val stopX = computeDayLeftPosition(mNumDays).toFloat() + var y = 0f + val deltaY = (mCellHeight + HOUR_GAP).toFloat() + var linesIndex = 0 + val startY = 0f + val stopY = (HOUR_GAP + 24 * (mCellHeight + HOUR_GAP)).toFloat() + var x = mHoursWidth.toFloat() // Draw the inner horizontal grid lines - p.setColor(mCalendarGridLineInnerHorizontalColor); - p.setStrokeWidth(GRID_LINE_INNER_WIDTH); - p.setAntiAlias(false); - y = 0; - linesIndex = 0; - for (int hour = 0; hour <= 24; hour++) { - mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN; - mLines[linesIndex++] = y; - mLines[linesIndex++] = stopX; - mLines[linesIndex++] = y; - y += deltaY; + p.setColor(mCalendarGridLineInnerHorizontalColor) + p.setStrokeWidth(GRID_LINE_INNER_WIDTH) + p.setAntiAlias(false) + y = 0f + linesIndex = 0 + for (hour in 0..24) { + mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN + mLines[linesIndex++] = y + mLines[linesIndex++] = stopX + mLines[linesIndex++] = y + y += deltaY } if (mCalendarGridLineInnerVerticalColor != mCalendarGridLineInnerHorizontalColor) { - canvas.drawLines(mLines, 0, linesIndex, p); - linesIndex = 0; - p.setColor(mCalendarGridLineInnerVerticalColor); + canvas.drawLines(mLines, 0, linesIndex, p) + linesIndex = 0 + p.setColor(mCalendarGridLineInnerVerticalColor) } // Draw the inner vertical grid lines - for (int day = 0; day <= mNumDays; day++) { - x = computeDayLeftPosition(day); - mLines[linesIndex++] = x; - mLines[linesIndex++] = startY; - mLines[linesIndex++] = x; - mLines[linesIndex++] = stopY; + for (day in 0..mNumDays) { + x = computeDayLeftPosition(day).toFloat() + mLines[linesIndex++] = x + mLines[linesIndex++] = startY + mLines[linesIndex++] = x + mLines[linesIndex++] = stopY } - canvas.drawLines(mLines, 0, linesIndex, p); + canvas.drawLines(mLines, 0, linesIndex, p) // Restore the saved style. - p.setStyle(savedStyle); - p.setAntiAlias(true); + p.setStyle(savedStyle) + p.setAntiAlias(true) } /** @@ -2315,366 +1871,359 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * @param canvas * @param p */ - private void drawBgColors(Rect r, Canvas canvas, Paint p) { - int todayIndex = mTodayJulianDay - mFirstJulianDay; + private fun drawBgColors(r: Rect, canvas: Canvas, p: Paint) { + val todayIndex = mTodayJulianDay - mFirstJulianDay // Draw the hours background color - r.top = mDestRect.top; - r.bottom = mDestRect.bottom; - r.left = 0; - r.right = mHoursWidth; - p.setColor(mBgColor); - p.setStyle(Style.FILL); - p.setAntiAlias(false); - canvas.drawRect(r, p); + r.top = mDestRect.top + r.bottom = mDestRect.bottom + r.left = 0 + r.right = mHoursWidth + p.setColor(mBgColor) + p.setStyle(Style.FILL) + p.setAntiAlias(false) + canvas.drawRect(r, p) // Draw background for grid area if (mNumDays == 1 && todayIndex == 0) { // Draw a white background for the time later than current time - int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP) - + ((mCurrentTime.minute * mCellHeight) / 60) + 1; + var lineY: Int = + mCurrentTime.hour * (mCellHeight + HOUR_GAP) + mCurrentTime.minute * mCellHeight / 60 + 1 if (lineY < mViewStartY + mViewHeight) { - lineY = Math.max(lineY, mViewStartY); - r.left = mHoursWidth; - r.right = mViewWidth; - r.top = lineY; - r.bottom = mViewStartY + mViewHeight; - p.setColor(mFutureBgColor); - canvas.drawRect(r, p); + lineY = Math.max(lineY, mViewStartY) + r.left = mHoursWidth + r.right = mViewWidth + r.top = lineY + r.bottom = mViewStartY + mViewHeight + p.setColor(mFutureBgColor) + canvas.drawRect(r, p) } } else if (todayIndex >= 0 && todayIndex < mNumDays) { // Draw today with a white background for the time later than current time - int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP) - + ((mCurrentTime.minute * mCellHeight) / 60) + 1; + var lineY: Int = + mCurrentTime.hour * (mCellHeight + HOUR_GAP) + mCurrentTime.minute * mCellHeight / 60 + 1 if (lineY < mViewStartY + mViewHeight) { - lineY = Math.max(lineY, mViewStartY); - r.left = computeDayLeftPosition(todayIndex) + 1; - r.right = computeDayLeftPosition(todayIndex + 1); - r.top = lineY; - r.bottom = mViewStartY + mViewHeight; - p.setColor(mFutureBgColor); - canvas.drawRect(r, p); + lineY = Math.max(lineY, mViewStartY) + r.left = computeDayLeftPosition(todayIndex) + 1 + r.right = computeDayLeftPosition(todayIndex + 1) + r.top = lineY + r.bottom = mViewStartY + mViewHeight + p.setColor(mFutureBgColor) + canvas.drawRect(r, p) } // Paint Tomorrow and later days with future color if (todayIndex + 1 < mNumDays) { - r.left = computeDayLeftPosition(todayIndex + 1) + 1; - r.right = computeDayLeftPosition(mNumDays); - r.top = mDestRect.top; - r.bottom = mDestRect.bottom; - p.setColor(mFutureBgColor); - canvas.drawRect(r, p); + r.left = computeDayLeftPosition(todayIndex + 1) + 1 + r.right = computeDayLeftPosition(mNumDays) + r.top = mDestRect.top + r.bottom = mDestRect.bottom + p.setColor(mFutureBgColor) + canvas.drawRect(r, p) } } else if (todayIndex < 0) { // Future - r.left = computeDayLeftPosition(0) + 1; - r.right = computeDayLeftPosition(mNumDays); - r.top = mDestRect.top; - r.bottom = mDestRect.bottom; - p.setColor(mFutureBgColor); - canvas.drawRect(r, p); + r.left = computeDayLeftPosition(0) + 1 + r.right = computeDayLeftPosition(mNumDays) + r.top = mDestRect.top + r.bottom = mDestRect.bottom + p.setColor(mFutureBgColor) + canvas.drawRect(r, p) } - p.setAntiAlias(true); + p.setAntiAlias(true) } - private int computeMaxStringWidth(int currentMax, String[] strings, Paint p) { - float maxWidthF = 0.0f; - - int len = strings.length; - for (int i = 0; i < len; i++) { - float width = p.measureText(strings[i]); - maxWidthF = Math.max(width, maxWidthF); + private fun computeMaxStringWidth(currentMax: Int, strings: Array, p: Paint): Int { + var maxWidthF = 0.0f + val len = strings.size + for (i in 0 until len) { + val width: Float = p.measureText(strings[i]) + maxWidthF = Math.max(width, maxWidthF) } - int maxWidth = (int) (maxWidthF + 0.5); + var maxWidth = (maxWidthF + 0.5).toInt() if (maxWidth < currentMax) { - maxWidth = currentMax; + maxWidth = currentMax } - return maxWidth; + return maxWidth } - private void saveSelectionPosition(float left, float top, float right, float bottom) { - mPrevBox.left = (int) left; - mPrevBox.right = (int) right; - mPrevBox.top = (int) top; - mPrevBox.bottom = (int) bottom; + private fun saveSelectionPosition(left: Float, top: Float, right: Float, bottom: Float) { + mPrevBox.left = left.toInt() + mPrevBox.right = right.toInt() + mPrevBox.top = top.toInt() + mPrevBox.bottom = bottom.toInt() } - private void setupTextRect(Rect r) { + private fun setupTextRect(r: Rect) { if (r.bottom <= r.top || r.right <= r.left) { - r.bottom = r.top; - r.right = r.left; - return; + r.bottom = r.top + r.right = r.left + return } - if (r.bottom - r.top > EVENT_TEXT_TOP_MARGIN + EVENT_TEXT_BOTTOM_MARGIN) { - r.top += EVENT_TEXT_TOP_MARGIN; - r.bottom -= EVENT_TEXT_BOTTOM_MARGIN; + r.top += EVENT_TEXT_TOP_MARGIN + r.bottom -= EVENT_TEXT_BOTTOM_MARGIN } if (r.right - r.left > EVENT_TEXT_LEFT_MARGIN + EVENT_TEXT_RIGHT_MARGIN) { - r.left += EVENT_TEXT_LEFT_MARGIN; - r.right -= EVENT_TEXT_RIGHT_MARGIN; + r.left += EVENT_TEXT_LEFT_MARGIN + r.right -= EVENT_TEXT_RIGHT_MARGIN } } - private void setupAllDayTextRect(Rect r) { + private fun setupAllDayTextRect(r: Rect) { if (r.bottom <= r.top || r.right <= r.left) { - r.bottom = r.top; - r.right = r.left; - return; + r.bottom = r.top + r.right = r.left + return } - if (r.bottom - r.top > EVENT_ALL_DAY_TEXT_TOP_MARGIN + EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN) { - r.top += EVENT_ALL_DAY_TEXT_TOP_MARGIN; - r.bottom -= EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN; + r.top += EVENT_ALL_DAY_TEXT_TOP_MARGIN + r.bottom -= EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN } if (r.right - r.left > EVENT_ALL_DAY_TEXT_LEFT_MARGIN + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN) { - r.left += EVENT_ALL_DAY_TEXT_LEFT_MARGIN; - r.right -= EVENT_ALL_DAY_TEXT_RIGHT_MARGIN; + r.left += EVENT_ALL_DAY_TEXT_LEFT_MARGIN + r.right -= EVENT_ALL_DAY_TEXT_RIGHT_MARGIN } } /** * Return the layout for a numbered event. Create it if not already existing */ - private StaticLayout getEventLayout(StaticLayout[] layouts, int i, Event event, Paint paint, - Rect r) { - if (i < 0 || i >= layouts.length) { - return null; - } - - StaticLayout layout = layouts[i]; + private fun getEventLayout( + layouts: Array?, i: Int, event: Event, paint: Paint, + r: Rect + ): StaticLayout? { + if (i < 0 || i >= layouts!!.size) { + return null + } + var layout: StaticLayout? = layouts!![i] // Check if we have already initialized the StaticLayout and that // the width hasn't changed (due to vertical resizing which causes // re-layout of events at min height) - if (layout == null || r.width() != layout.getWidth()) { - SpannableStringBuilder bob = new SpannableStringBuilder(); + if (layout == null || r.width() !== layout.getWidth()) { + val bob = SpannableStringBuilder() if (event.title != null) { // MAX - 1 since we add a space - bob.append(drawTextSanitizer(event.title.toString(), MAX_EVENT_TEXT_LEN - 1)); - bob.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length(), 0); - bob.append(' '); + bob.append(drawTextSanitizer(event.title.toString(), MAX_EVENT_TEXT_LEN - 1)) + bob.setSpan(StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length(), 0) + bob.append(' ') } if (event.location != null) { - bob.append(drawTextSanitizer(event.location.toString(), - MAX_EVENT_TEXT_LEN - bob.length())); - } - - switch (event.selfAttendeeStatus) { - case Attendees.ATTENDEE_STATUS_INVITED: - paint.setColor(event.color); - break; - case Attendees.ATTENDEE_STATUS_DECLINED: - paint.setColor(mEventTextColor); - paint.setAlpha(Utils.DECLINED_EVENT_TEXT_ALPHA); - break; - case Attendees.ATTENDEE_STATUS_NONE: // Your own events - case Attendees.ATTENDEE_STATUS_ACCEPTED: - case Attendees.ATTENDEE_STATUS_TENTATIVE: - default: - paint.setColor(mEventTextColor); - break; + bob.append( + drawTextSanitizer( + event.location.toString(), + MAX_EVENT_TEXT_LEN - bob.length() + ) + ) + } + when (event.selfAttendeeStatus) { + Attendees.ATTENDEE_STATUS_INVITED -> paint.setColor(event.color) + Attendees.ATTENDEE_STATUS_DECLINED -> { + paint.setColor(mEventTextColor) + paint.setAlpha(Utils.DECLINED_EVENT_TEXT_ALPHA) + } + Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED, Attendees.ATTENDEE_STATUS_TENTATIVE -> paint.setColor( + mEventTextColor + ) + else -> paint.setColor(mEventTextColor) } // Leave a one pixel boundary on the left and right of the rectangle for the event - layout = new StaticLayout(bob, 0, bob.length(), new TextPaint(paint), r.width(), - Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null, r.width()); - - layouts[i] = layout; + layout = StaticLayout( + bob, 0, bob.length(), TextPaint(paint), r.width(), + Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null, r.width() + ) + layouts[i] = layout } - layout.getPaint().setAlpha(mEventsAlpha); - return layout; + layout.getPaint().setAlpha(mEventsAlpha) + return layout } - private void drawAllDayEvents(int firstDay, int numDays, Canvas canvas, Paint p) { - - p.setTextSize(NORMAL_FONT_SIZE); - p.setTextAlign(Paint.Align.LEFT); - Paint eventTextPaint = mEventTextPaint; - - final float startY = DAY_HEADER_HEIGHT; - final float stopY = startY + mAlldayHeight + ALLDAY_TOP_MARGIN; - float x = 0; - int linesIndex = 0; + private fun drawAllDayEvents(firstDay: Int, numDays: Int, canvas: Canvas, p: Paint) { + p.setTextSize(NORMAL_FONT_SIZE) + p.setTextAlign(Paint.Align.LEFT) + val eventTextPaint: Paint = mEventTextPaint + val startY = DAY_HEADER_HEIGHT.toFloat() + val stopY = startY + mAlldayHeight + ALLDAY_TOP_MARGIN + var x = 0f + var linesIndex = 0 // Draw the inner vertical grid lines - p.setColor(mCalendarGridLineInnerVerticalColor); - x = mHoursWidth; - p.setStrokeWidth(GRID_LINE_INNER_WIDTH); + p.setColor(mCalendarGridLineInnerVerticalColor) + x = mHoursWidth.toFloat() + p.setStrokeWidth(GRID_LINE_INNER_WIDTH) // Line bounding the top of the all day area - mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN; - mLines[linesIndex++] = startY; - mLines[linesIndex++] = computeDayLeftPosition(mNumDays); - mLines[linesIndex++] = startY; - - for (int day = 0; day <= mNumDays; day++) { - x = computeDayLeftPosition(day); - mLines[linesIndex++] = x; - mLines[linesIndex++] = startY; - mLines[linesIndex++] = x; - mLines[linesIndex++] = stopY; - } - p.setAntiAlias(false); - canvas.drawLines(mLines, 0, linesIndex, p); - p.setStyle(Style.FILL); - - int y = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN; - int lastDay = firstDay + numDays - 1; - final ArrayList events = mAllDayEvents; - int numEvents = events.size(); + mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN + mLines[linesIndex++] = startY + mLines[linesIndex++] = computeDayLeftPosition(mNumDays).toFloat() + mLines[linesIndex++] = startY + for (day in 0..mNumDays) { + x = computeDayLeftPosition(day).toFloat() + mLines[linesIndex++] = x + mLines[linesIndex++] = startY + mLines[linesIndex++] = x + mLines[linesIndex++] = stopY + } + p.setAntiAlias(false) + canvas.drawLines(mLines, 0, linesIndex, p) + p.setStyle(Style.FILL) + val y = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN + val lastDay = firstDay + numDays - 1 + val events: ArrayList? = mAllDayEvents + val numEvents: Int = events.size() // Whether or not we should draw the more events text - boolean hasMoreEvents = false; + var hasMoreEvents = false // size of the allDay area - float drawHeight = mAlldayHeight; + val drawHeight = mAlldayHeight.toFloat() // max number of events being drawn in one day of the allday area - float numRectangles = mMaxAlldayEvents; + var numRectangles = mMaxAlldayEvents.toFloat() // Where to cut off drawn allday events - int allDayEventClip = DAY_HEADER_HEIGHT + mAlldayHeight + ALLDAY_TOP_MARGIN; + var allDayEventClip = DAY_HEADER_HEIGHT + mAlldayHeight + ALLDAY_TOP_MARGIN // The number of events that weren't drawn in each day - mSkippedAlldayEvents = new int[numDays]; - if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount && !mShowAllAllDayEvents && - mAnimateDayHeight == 0) { + mSkippedAlldayEvents = IntArray(numDays) + if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount && !mShowAllAllDayEvents && mAnimateDayHeight == 0) { // We draw one fewer event than will fit so that more events text // can be drawn - numRectangles = mMaxUnexpandedAlldayEventCount - 1; + numRectangles = (mMaxUnexpandedAlldayEventCount - 1).toFloat() // We also clip the events above the more events text - allDayEventClip -= MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; - hasMoreEvents = true; + allDayEventClip -= MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt() + hasMoreEvents = true } else if (mAnimateDayHeight != 0) { // clip at the end of the animating space - allDayEventClip = DAY_HEADER_HEIGHT + mAnimateDayHeight + ALLDAY_TOP_MARGIN; - } - - int alpha = eventTextPaint.getAlpha(); - eventTextPaint.setAlpha(mEventsAlpha); - for (int i = 0; i < numEvents; i++) { - Event event = events.get(i); - int startDay = event.startDay; - int endDay = event.endDay; + allDayEventClip = DAY_HEADER_HEIGHT + mAnimateDayHeight + ALLDAY_TOP_MARGIN + } + var alpha: Int = eventTextPaint.getAlpha() + eventTextPaint.setAlpha(mEventsAlpha) + for (i in 0 until numEvents) { + val event: Event = events.get(i) + var startDay: Int = event.startDay + var endDay: Int = event.endDay if (startDay > lastDay || endDay < firstDay) { - continue; + continue } if (startDay < firstDay) { - startDay = firstDay; + startDay = firstDay } if (endDay > lastDay) { - endDay = lastDay; + endDay = lastDay } - int startIndex = startDay - firstDay; - int endIndex = endDay - firstDay; - float height = mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount ? mAnimateDayEventHeight : - drawHeight / numRectangles; + val startIndex = startDay - firstDay + val endIndex = endDay - firstDay + var height = + if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) mAnimateDayEventHeight.toFloat() else drawHeight / numRectangles // Prevent a single event from getting too big if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) { - height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; + height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT.toFloat() } // Leave a one-pixel space between the vertical day lines and the // event rectangle. - event.left = computeDayLeftPosition(startIndex); - event.right = computeDayLeftPosition(endIndex + 1) - DAY_GAP; - event.top = y + height * event.getColumn(); - event.bottom = event.top + height - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN; + event.left = computeDayLeftPosition(startIndex) + event.right = computeDayLeftPosition(endIndex + 1) - DAY_GAP + event.top = y + height * event.getColumn() + event.bottom = event.top + height - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { // check if we should skip this event. We skip if it starts // after the clip bound or ends after the skip bound and we're // not animating. if (event.top >= allDayEventClip) { - incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex); - continue; + incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex) + continue } else if (event.bottom > allDayEventClip) { if (hasMoreEvents) { - incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex); - continue; + incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex) + continue } - event.bottom = allDayEventClip; + event.bottom = allDayEventClip } } - Rect r = drawEventRect(event, canvas, p, eventTextPaint, (int) event.top, - (int) event.bottom); - setupAllDayTextRect(r); - StaticLayout layout = getEventLayout(mAllDayLayouts, i, event, eventTextPaint, r); - drawEventText(layout, r, canvas, r.top, r.bottom, true); + val r: Rect = drawEventRect( + event, canvas, p, eventTextPaint, event.top as Int, + event.bottom as Int + ) + setupAllDayTextRect(r) + val layout: StaticLayout? = getEventLayout(mAllDayLayouts, i, event, eventTextPaint, r) + drawEventText(layout, r, canvas, r.top, r.bottom, true) // Check if this all-day event intersects the selected day if (mSelectionAllday && mComputeSelectedEvents) { if (startDay <= mSelectionDay && endDay >= mSelectionDay) { - mSelectedEvents.add(event); + mSelectedEvents.add(event) } } } - eventTextPaint.setAlpha(alpha); - + eventTextPaint.setAlpha(alpha) if (mMoreAlldayEventsTextAlpha != 0 && mSkippedAlldayEvents != null) { // If the more allday text should be visible, draw it. - alpha = p.getAlpha(); - p.setAlpha(mEventsAlpha); - p.setColor(mMoreAlldayEventsTextAlpha << 24 & mMoreEventsTextColor); - for (int i = 0; i < mSkippedAlldayEvents.length; i++) { - if (mSkippedAlldayEvents[i] > 0) { - drawMoreAlldayEvents(canvas, mSkippedAlldayEvents[i], i, p); + alpha = p.getAlpha() + p.setAlpha(mEventsAlpha) + p.setColor(mMoreAlldayEventsTextAlpha shl 24 and mMoreEventsTextColor) + for (i in mSkippedAlldayEvents.indices) { + if (mSkippedAlldayEvents!![i] > 0) { + drawMoreAlldayEvents(canvas, mSkippedAlldayEvents!![i], i, p) } } - p.setAlpha(alpha); + p.setAlpha(alpha) } - if (mSelectionAllday) { // Compute the neighbors for the list of all-day events that // intersect the selected day. - computeAllDayNeighbors(); + computeAllDayNeighbors() // Set the selection position to zero so that when we move down // to the normal event area, we will highlight the topmost event. - saveSelectionPosition(0f, 0f, 0f, 0f); + saveSelectionPosition(0f, 0f, 0f, 0f) } } // Helper method for counting the number of allday events skipped on each day - private void incrementSkipCount(int[] counts, int startIndex, int endIndex) { - if (counts == null || startIndex < 0 || endIndex > counts.length) { - return; + private fun incrementSkipCount(counts: IntArray?, startIndex: Int, endIndex: Int) { + if (counts == null || startIndex < 0 || endIndex > counts.size) { + return } - for (int i = startIndex; i <= endIndex; i++) { - counts[i]++; + for (i in startIndex..endIndex) { + counts[i]++ } } // Draws the "box +n" text for hidden allday events - protected void drawMoreAlldayEvents(Canvas canvas, int remainingEvents, int day, Paint p) { - int x = computeDayLeftPosition(day) + EVENT_ALL_DAY_TEXT_LEFT_MARGIN; - int y = (int) (mAlldayHeight - .5f * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - .5f - * EVENT_SQUARE_WIDTH + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN); - Rect r = mRect; - r.top = y; - r.left = x; - r.bottom = y + EVENT_SQUARE_WIDTH; - r.right = x + EVENT_SQUARE_WIDTH; - p.setColor(mMoreEventsTextColor); - p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH); - p.setStyle(Style.STROKE); - p.setAntiAlias(false); - canvas.drawRect(r, p); - p.setAntiAlias(true); - p.setStyle(Style.FILL); - p.setTextSize(EVENT_TEXT_FONT_SIZE); - String text = mResources.getQuantityString(R.plurals.month_more_events, remainingEvents); - y += EVENT_SQUARE_WIDTH; - x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING; - canvas.drawText(String.format(text, remainingEvents), x, y, p); - } - - private void computeAllDayNeighbors() { - int len = mSelectedEvents.size(); + protected fun drawMoreAlldayEvents(canvas: Canvas, remainingEvents: Int, day: Int, p: Paint) { + var x = computeDayLeftPosition(day) + EVENT_ALL_DAY_TEXT_LEFT_MARGIN + var y = (mAlldayHeight - .5f * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - (.5f + * EVENT_SQUARE_WIDTH) + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN).toInt() + val r: Rect = mRect + r.top = y + r.left = x + r.bottom = y + EVENT_SQUARE_WIDTH + r.right = x + EVENT_SQUARE_WIDTH + p.setColor(mMoreEventsTextColor) + p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH) + p.setStyle(Style.STROKE) + p.setAntiAlias(false) + canvas.drawRect(r, p) + p.setAntiAlias(true) + p.setStyle(Style.FILL) + p.setTextSize(EVENT_TEXT_FONT_SIZE) + val text: String = + mResources.getQuantityString(R.plurals.month_more_events, remainingEvents) + y += EVENT_SQUARE_WIDTH + x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING + canvas.drawText(String.format(text, remainingEvents), x, y, p) + } + + private fun computeAllDayNeighbors() { + val len: Int = mSelectedEvents.size() if (len == 0 || mSelectedEvent != null) { - return; + return } // First, clear all the links - for (int ii = 0; ii < len; ii++) { - Event ev = mSelectedEvents.get(ii); - ev.nextUp = null; - ev.nextDown = null; - ev.nextLeft = null; - ev.nextRight = null; + for (ii in 0 until len) { + val ev: Event = mSelectedEvents.get(ii) + ev.nextUp = null + ev.nextDown = null + ev.nextLeft = null + ev.nextRight = null } // For each event in the selected event list "mSelectedEvents", find @@ -2684,294 +2233,295 @@ public class DayView extends View implements View.OnCreateContextMenuListener, // Find the event in the same row as the previously selected all-day // event, if any. - int startPosition = -1; + var startPosition = -1 if (mPrevSelectedEvent != null && mPrevSelectedEvent.drawAsAllday()) { - startPosition = mPrevSelectedEvent.getColumn(); - } - int maxPosition = -1; - Event startEvent = null; - Event maxPositionEvent = null; - for (int ii = 0; ii < len; ii++) { - Event ev = mSelectedEvents.get(ii); - int position = ev.getColumn(); + startPosition = mPrevSelectedEvent.getColumn() + } + var maxPosition = -1 + var startEvent: Event? = null + var maxPositionEvent: Event? = null + for (ii in 0 until len) { + val ev: Event = mSelectedEvents.get(ii) + val position: Int = ev.getColumn() if (position == startPosition) { - startEvent = ev; + startEvent = ev } else if (position > maxPosition) { - maxPositionEvent = ev; - maxPosition = position; + maxPositionEvent = ev + maxPosition = position } - for (int jj = 0; jj < len; jj++) { + for (jj in 0 until len) { if (jj == ii) { - continue; + continue } - Event neighbor = mSelectedEvents.get(jj); - int neighborPosition = neighbor.getColumn(); + val neighbor: Event = mSelectedEvents.get(jj) + val neighborPosition: Int = neighbor.getColumn() if (neighborPosition == position - 1) { - ev.nextUp = neighbor; + ev.nextUp = neighbor } else if (neighborPosition == position + 1) { - ev.nextDown = neighbor; + ev.nextDown = neighbor } } } if (startEvent != null) { - setSelectedEvent(startEvent); + setSelectedEvent(startEvent) } else { - setSelectedEvent(maxPositionEvent); + setSelectedEvent(maxPositionEvent) } } - private void drawEvents(int date, int dayIndex, int top, Canvas canvas, Paint p) { - Paint eventTextPaint = mEventTextPaint; - int left = computeDayLeftPosition(dayIndex) + 1; - int cellWidth = computeDayLeftPosition(dayIndex + 1) - left + 1; - int cellHeight = mCellHeight; + private fun drawEvents(date: Int, dayIndex: Int, top: Int, canvas: Canvas, p: Paint) { + val eventTextPaint: Paint = mEventTextPaint + val left = computeDayLeftPosition(dayIndex) + 1 + val cellWidth = computeDayLeftPosition(dayIndex + 1) - left + 1 + val cellHeight = mCellHeight // Use the selected hour as the selection region - Rect selectionArea = mSelectionRect; - selectionArea.top = top + mSelectionHour * (cellHeight + HOUR_GAP); - selectionArea.bottom = selectionArea.top + cellHeight; - selectionArea.left = left; - selectionArea.right = selectionArea.left + cellWidth; - - final ArrayList events = mEvents; - int numEvents = events.size(); - EventGeometry geometry = mEventGeometry; - - final int viewEndY = mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight; - - int alpha = eventTextPaint.getAlpha(); - eventTextPaint.setAlpha(mEventsAlpha); - for (int i = 0; i < numEvents; i++) { - Event event = events.get(i); + val selectionArea: Rect = mSelectionRect + selectionArea.top = top + mSelectionHour * (cellHeight + HOUR_GAP) + selectionArea.bottom = selectionArea.top + cellHeight + selectionArea.left = left + selectionArea.right = selectionArea.left + cellWidth + val events: ArrayList = mEvents + val numEvents: Int = events.size() + val geometry: EventGeometry = mEventGeometry + val viewEndY = mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight + val alpha: Int = eventTextPaint.getAlpha() + eventTextPaint.setAlpha(mEventsAlpha) + for (i in 0 until numEvents) { + val event: Event = events.get(i) if (!geometry.computeEventRect(date, left, top, cellWidth, event)) { - continue; + continue } // Don't draw it if it is not visible if (event.bottom < mViewStartY || event.top > viewEndY) { - continue; + continue } - if (date == mSelectionDay && !mSelectionAllday && mComputeSelectedEvents - && geometry.eventIntersectsSelection(event, selectionArea)) { - mSelectedEvents.add(event); + && geometry.eventIntersectsSelection(event, selectionArea) + ) { + mSelectedEvents.add(event) } - - Rect r = drawEventRect(event, canvas, p, eventTextPaint, mViewStartY, viewEndY); - setupTextRect(r); + val r: Rect = drawEventRect(event, canvas, p, eventTextPaint, mViewStartY, viewEndY) + setupTextRect(r) // Don't draw text if it is not visible if (r.top > viewEndY || r.bottom < mViewStartY) { - continue; + continue } - StaticLayout layout = getEventLayout(mLayouts, i, event, eventTextPaint, r); + val layout: StaticLayout? = getEventLayout(mLayouts, i, event, eventTextPaint, r) // TODO: not sure why we are 4 pixels off - drawEventText(layout, r, canvas, mViewStartY + 4, mViewStartY + mViewHeight - - DAY_HEADER_HEIGHT - mAlldayHeight, false); - } - eventTextPaint.setAlpha(alpha); - } - - private Rect drawEventRect(Event event, Canvas canvas, Paint p, Paint eventTextPaint, - int visibleTop, int visibleBot) { + drawEventText( + layout, + r, + canvas, + mViewStartY + 4, + mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight, + false + ) + } + eventTextPaint.setAlpha(alpha) + } + + private fun drawEventRect( + event: Event, canvas: Canvas, p: Paint, eventTextPaint: Paint, + visibleTop: Int, visibleBot: Int + ): Rect { // Draw the Event Rect - Rect r = mRect; - r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN, visibleTop); - r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN, visibleBot); - r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN; - r.right = (int) event.right; - - int color = event.color; - switch (event.selfAttendeeStatus) { - case Attendees.ATTENDEE_STATUS_INVITED: - if (event != mClickedEvent) { - p.setStyle(Style.STROKE); + val r: Rect = mRect + r.top = Math.max(event.top as Int + EVENT_RECT_TOP_MARGIN, visibleTop) + r.bottom = Math.min(event.bottom as Int - EVENT_RECT_BOTTOM_MARGIN, visibleBot) + r.left = event.left as Int + EVENT_RECT_LEFT_MARGIN + r.right = event.right + var color: Int = event.color + when (event.selfAttendeeStatus) { + Attendees.ATTENDEE_STATUS_INVITED -> if (event !== mClickedEvent) { + p.setStyle(Style.STROKE) + } + Attendees.ATTENDEE_STATUS_DECLINED -> { + if (event !== mClickedEvent) { + color = Utils.getDeclinedColorFromColor(color) } - break; - case Attendees.ATTENDEE_STATUS_DECLINED: - if (event != mClickedEvent) { - color = Utils.getDeclinedColorFromColor(color); - } - case Attendees.ATTENDEE_STATUS_NONE: // Your own events - case Attendees.ATTENDEE_STATUS_ACCEPTED: - case Attendees.ATTENDEE_STATUS_TENTATIVE: - default: - p.setStyle(Style.FILL_AND_STROKE); - break; - } - - p.setAntiAlias(false); - - int floorHalfStroke = (int) Math.floor(EVENT_RECT_STROKE_WIDTH / 2.0f); - int ceilHalfStroke = (int) Math.ceil(EVENT_RECT_STROKE_WIDTH / 2.0f); - r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN + floorHalfStroke, visibleTop); - r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN - ceilHalfStroke, - visibleBot); - r.left += floorHalfStroke; - r.right -= ceilHalfStroke; - p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH); - p.setColor(color); - int alpha = p.getAlpha(); - p.setAlpha(mEventsAlpha); - canvas.drawRect(r, p); - p.setAlpha(alpha); - p.setStyle(Style.FILL); + p.setStyle(Style.FILL_AND_STROKE) + } + Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED, Attendees.ATTENDEE_STATUS_TENTATIVE -> p.setStyle( + Style.FILL_AND_STROKE + ) + else -> p.setStyle(Style.FILL_AND_STROKE) + } + p.setAntiAlias(false) + val floorHalfStroke = Math.floor(EVENT_RECT_STROKE_WIDTH / 2.0f) as Int + val ceilHalfStroke = Math.ceil(EVENT_RECT_STROKE_WIDTH / 2.0f) as Int + r.top = Math.max(event.top as Int + EVENT_RECT_TOP_MARGIN + floorHalfStroke, visibleTop) + r.bottom = Math.min( + event.bottom as Int - EVENT_RECT_BOTTOM_MARGIN - ceilHalfStroke, + visibleBot + ) + r.left += floorHalfStroke + r.right -= ceilHalfStroke + p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH) + p.setColor(color) + val alpha: Int = p.getAlpha() + p.setAlpha(mEventsAlpha) + canvas.drawRect(r, p) + p.setAlpha(alpha) + p.setStyle(Style.FILL) // Setup rect for drawEventText which follows - r.top = (int) event.top + EVENT_RECT_TOP_MARGIN; - r.bottom = (int) event.bottom - EVENT_RECT_BOTTOM_MARGIN; - r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN; - r.right = (int) event.right - EVENT_RECT_RIGHT_MARGIN; - return r; + r.top = event.top as Int + EVENT_RECT_TOP_MARGIN + r.bottom = event.bottom as Int - EVENT_RECT_BOTTOM_MARGIN + r.left = event.left as Int + EVENT_RECT_LEFT_MARGIN + r.right = event.right as Int - EVENT_RECT_RIGHT_MARGIN + return r } - private final Pattern drawTextSanitizerFilter = Pattern.compile("[\t\n],"); + private val drawTextSanitizerFilter: Pattern = Pattern.compile("[\t\n],") // Sanitize a string before passing it to drawText or else we get little // squares. For newlines and tabs before a comma, delete the character. // Otherwise, just replace them with a space. - private String drawTextSanitizer(String string, int maxEventTextLen) { - Matcher m = drawTextSanitizerFilter.matcher(string); - string = m.replaceAll(","); - - int len = string.length(); + private fun drawTextSanitizer(string: String, maxEventTextLen: Int): String { + var string = string + val m: Matcher = drawTextSanitizerFilter.matcher(string) + string = m.replaceAll(",") + var len: Int = string.length() if (maxEventTextLen <= 0) { - string = ""; - len = 0; + string = "" + len = 0 } else if (len > maxEventTextLen) { - string = string.substring(0, maxEventTextLen); - len = maxEventTextLen; + string = string.substring(0, maxEventTextLen) + len = maxEventTextLen } - - return string.replace('\n', ' '); + return string.replace('\n', ' ') } - private void drawEventText(StaticLayout eventLayout, Rect rect, Canvas canvas, int top, - int bottom, boolean center) { + private fun drawEventText( + eventLayout: StaticLayout?, rect: Rect, canvas: Canvas, top: Int, + bottom: Int, center: Boolean + ) { // drawEmptyRect(canvas, rect, 0xFFFF00FF); // for debugging - - int width = rect.right - rect.left; - int height = rect.bottom - rect.top; + val width: Int = rect.right - rect.left + val height: Int = rect.bottom - rect.top // If the rectangle is too small for text, then return if (eventLayout == null || width < MIN_CELL_WIDTH_FOR_TEXT) { - return; - } - - int totalLineHeight = 0; - int lineCount = eventLayout.getLineCount(); - for (int i = 0; i < lineCount; i++) { - int lineBottom = eventLayout.getLineBottom(i); - if (lineBottom <= height) { - totalLineHeight = lineBottom; + return + } + var totalLineHeight = 0 + val lineCount: Int = eventLayout.getLineCount() + for (i in 0 until lineCount) { + val lineBottom: Int = eventLayout.getLineBottom(i) + totalLineHeight = if (lineBottom <= height) { + lineBottom } else { - break; + break } } - // + 2 is small workaround when the font is slightly bigger then the rect. This will + // + 2 is small workaround when the font is slightly bigger than the rect. This will // still allow the text to be shown without overflowing into the other all day rects. if (totalLineHeight == 0 || rect.top > bottom || rect.top + totalLineHeight + 2 < top) { - return; + return } // Use a StaticLayout to format the string. - canvas.save(); - // canvas.translate(rect.left, rect.top + (rect.bottom - rect.top / 2)); - int padding = center? (rect.bottom - rect.top - totalLineHeight) / 2 : 0; - canvas.translate(rect.left, rect.top + padding); - rect.left = 0; - rect.right = width; - rect.top = 0; - rect.bottom = totalLineHeight; + canvas.save() + // canvas.translate(rect.left, rect.top + (rect.bottom - rect.top / 2)); + val padding = if (center) (rect.bottom - rect.top - totalLineHeight) / 2 else 0 + canvas.translate(rect.left, rect.top + padding) + rect.left = 0 + rect.right = width + rect.top = 0 + rect.bottom = totalLineHeight // There's a bug somewhere. If this rect is outside of a previous // cliprect, this becomes a no-op. What happens is that the text draw // past the event rect. The current fix is to not draw the staticLayout // at all if it is completely out of bound. - canvas.clipRect(rect); - eventLayout.draw(canvas); - canvas.restore(); + canvas.clipRect(rect) + eventLayout.draw(canvas) + canvas.restore() } // The following routines are called from the parent activity when certain // touch events occur. - private void doDown(MotionEvent ev) { - mTouchMode = TOUCH_MODE_DOWN; - mViewStartX = 0; - mOnFlingCalled = false; - mHandler.removeCallbacks(mContinueScroll); - int x = (int) ev.getX(); - int y = (int) ev.getY(); + private fun doDown(ev: MotionEvent) { + mTouchMode = TOUCH_MODE_DOWN + mViewStartX = 0 + mOnFlingCalled = false + mHandler.removeCallbacks(mContinueScroll) + val x = ev.getX() as Int + val y = ev.getY() as Int // Save selection information: we use setSelectionFromPosition to find the selected event // in order to show the "clicked" color. But since it is also setting the selected info // for new events, we need to restore the old info after calling the function. - Event oldSelectedEvent = mSelectedEvent; - int oldSelectionDay = mSelectionDay; - int oldSelectionHour = mSelectionHour; + val oldSelectedEvent: Event? = mSelectedEvent + val oldSelectionDay = mSelectionDay + val oldSelectionHour = mSelectionHour if (setSelectionFromPosition(x, y, false)) { // If a time was selected (a blue selection box is visible) and the click location // is in the selected time, do not show a click on an event to prevent a situation // of both a selection and an event are clicked when they overlap. - boolean pressedSelected = (mSelectionMode != SELECTION_HIDDEN) - && oldSelectionDay == mSelectionDay && oldSelectionHour == mSelectionHour; + val pressedSelected = (mSelectionMode != SELECTION_HIDDEN + && oldSelectionDay == mSelectionDay && oldSelectionHour == mSelectionHour) if (!pressedSelected && mSelectedEvent != null) { - mSavedClickedEvent = mSelectedEvent; - mDownTouchTime = System.currentTimeMillis(); - postDelayed (mSetClick,mOnDownDelay); + mSavedClickedEvent = mSelectedEvent + mDownTouchTime = System.currentTimeMillis() + postDelayed(mSetClick, mOnDownDelay) } else { - eventClickCleanup(); + eventClickCleanup() } } - mSelectedEvent = oldSelectedEvent; - mSelectionDay = oldSelectionDay; - mSelectionHour = oldSelectionHour; - invalidate(); + mSelectedEvent = oldSelectedEvent + mSelectionDay = oldSelectionDay + mSelectionHour = oldSelectionHour + invalidate() } // Kicks off all the animations when the expand allday area is tapped - private void doExpandAllDayClick() { - mShowAllAllDayEvents = !mShowAllAllDayEvents; - - ObjectAnimator.setFrameDelay(0); + private fun doExpandAllDayClick() { + mShowAllAllDayEvents = !mShowAllAllDayEvents + ObjectAnimator.setFrameDelay(0) // Determine the starting height if (mAnimateDayHeight == 0) { - mAnimateDayHeight = mShowAllAllDayEvents ? - mAlldayHeight - (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT : mAlldayHeight; + mAnimateDayHeight = + if (mShowAllAllDayEvents) mAlldayHeight - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt() else mAlldayHeight } // Cancel current animations - mCancellingAnimations = true; + mCancellingAnimations = true if (mAlldayAnimator != null) { - mAlldayAnimator.cancel(); + mAlldayAnimator.cancel() } if (mAlldayEventAnimator != null) { - mAlldayEventAnimator.cancel(); + mAlldayEventAnimator.cancel() } if (mMoreAlldayEventsAnimator != null) { - mMoreAlldayEventsAnimator.cancel(); + mMoreAlldayEventsAnimator.cancel() } - mCancellingAnimations = false; + mCancellingAnimations = false // get new animators - mAlldayAnimator = getAllDayAnimator(); - mAlldayEventAnimator = getAllDayEventAnimator(); - mMoreAlldayEventsAnimator = ObjectAnimator.ofInt(this, - "moreAllDayEventsTextAlpha", - mShowAllAllDayEvents ? MORE_EVENTS_MAX_ALPHA : 0, - mShowAllAllDayEvents ? 0 : MORE_EVENTS_MAX_ALPHA); + mAlldayAnimator = allDayAnimator + mAlldayEventAnimator = allDayEventAnimator + mMoreAlldayEventsAnimator = ObjectAnimator.ofInt( + this, + "moreAllDayEventsTextAlpha", + if (mShowAllAllDayEvents) MORE_EVENTS_MAX_ALPHA else 0, + if (mShowAllAllDayEvents) 0 else MORE_EVENTS_MAX_ALPHA + ) // Set up delays and start the animators - mAlldayAnimator.setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0); - mAlldayAnimator.start(); - mMoreAlldayEventsAnimator.setStartDelay(mShowAllAllDayEvents ? 0 : ANIMATION_DURATION); - mMoreAlldayEventsAnimator.setDuration(ANIMATION_SECONDARY_DURATION); - mMoreAlldayEventsAnimator.start(); + mAlldayAnimator.setStartDelay(if (mShowAllAllDayEvents) ANIMATION_SECONDARY_DURATION else 0) + mAlldayAnimator.start() + mMoreAlldayEventsAnimator.setStartDelay(if (mShowAllAllDayEvents) 0 else ANIMATION_DURATION) + mMoreAlldayEventsAnimator.setDuration(ANIMATION_SECONDARY_DURATION) + mMoreAlldayEventsAnimator.start() if (mAlldayEventAnimator != null) { // This is the only animator that can return null, so check it mAlldayEventAnimator - .setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0); - mAlldayEventAnimator.start(); + .setStartDelay(if (mShowAllAllDayEvents) ANIMATION_SECONDARY_DURATION else 0) + mAlldayEventAnimator.start() } } @@ -2979,570 +2529,579 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * Figures out the initial heights for allDay events and space when * a view is being set up. */ - public void initAllDayHeights() { + fun initAllDayHeights() { if (mMaxAlldayEvents <= mMaxUnexpandedAlldayEventCount) { - return; + return } if (mShowAllAllDayEvents) { - int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; - maxADHeight = Math.min(maxADHeight, - (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)); - mAnimateDayEventHeight = maxADHeight / mMaxAlldayEvents; + var maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT + maxADHeight = Math.min( + maxADHeight, + (mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt() + ) + mAnimateDayEventHeight = maxADHeight / mMaxAlldayEvents } else { - mAnimateDayEventHeight = (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; + mAnimateDayEventHeight = MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt() } - } + }// First calculate the absolute max height + // Now expand to fit but not beyond the absolute max + // calculate the height of individual events in order to fit + // if there's nothing to animate just return + // Set up the animator with the calculated values // Sets up an animator for changing the height of allday events - private ObjectAnimator getAllDayEventAnimator() { - // First calculate the absolute max height - int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; - // Now expand to fit but not beyond the absolute max - maxADHeight = - Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)); - // calculate the height of individual events in order to fit - int fitHeight = maxADHeight / mMaxAlldayEvents; - int currentHeight = mAnimateDayEventHeight; - int desiredHeight = - mShowAllAllDayEvents ? fitHeight : (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; - // if there's nothing to animate just return - if (currentHeight == desiredHeight) { - return null; - } - - // Set up the animator with the calculated values - ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayEventHeight", - currentHeight, desiredHeight); - animator.setDuration(ANIMATION_DURATION); - return animator; - } - + private val allDayEventAnimator: ObjectAnimator? + private get() { + // First calculate the absolute max height + var maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT + // Now expand to fit but not beyond the absolute max + maxADHeight = Math.min( + maxADHeight, + (mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt() + ) + // calculate the height of individual events in order to fit + val fitHeight = maxADHeight / mMaxAlldayEvents + val currentHeight = mAnimateDayEventHeight + val desiredHeight = + if (mShowAllAllDayEvents) fitHeight else MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt() + // if there's nothing to animate just return + if (currentHeight == desiredHeight) { + return null + } + + // Set up the animator with the calculated values + val animator: ObjectAnimator = ObjectAnimator.ofInt( + this, "animateDayEventHeight", + currentHeight, desiredHeight + ) + animator.setDuration(ANIMATION_DURATION) + return animator + }// when finished, set this to 0 to signify not animating// Calculate the absolute max height + // Find the desired height but don't exceed abs max + // calculate the current and desired heights + + // Set up the animator with the calculated values // Sets up an animator for changing the height of the allday area - private ObjectAnimator getAllDayAnimator() { - // Calculate the absolute max height - int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; - // Find the desired height but don't exceed abs max - maxADHeight = - Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)); - // calculate the current and desired heights - int currentHeight = mAnimateDayHeight != 0 ? mAnimateDayHeight : mAlldayHeight; - int desiredHeight = mShowAllAllDayEvents ? maxADHeight : - (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - 1); - - // Set up the animator with the calculated values - ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayHeight", - currentHeight, desiredHeight); - animator.setDuration(ANIMATION_DURATION); - - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (!mCancellingAnimations) { - // when finished, set this to 0 to signify not animating - mAnimateDayHeight = 0; - mUseExpandIcon = !mShowAllAllDayEvents; + private val allDayAnimator: ObjectAnimator + private get() { + // Calculate the absolute max height + var maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT + // Find the desired height but don't exceed abs max + maxADHeight = Math.min( + maxADHeight, + (mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt() + ) + // calculate the current and desired heights + val currentHeight = if (mAnimateDayHeight != 0) mAnimateDayHeight else mAlldayHeight + val desiredHeight = + if (mShowAllAllDayEvents) maxADHeight else (MAX_UNEXPANDED_ALLDAY_HEIGHT - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - 1).toInt() + + // Set up the animator with the calculated values + val animator: ObjectAnimator = ObjectAnimator.ofInt( + this, "animateDayHeight", + currentHeight, desiredHeight + ) + animator.setDuration(ANIMATION_DURATION) + animator.addListener(object : AnimatorListenerAdapter() { + @Override + fun onAnimationEnd(animation: Animator?) { + if (!mCancellingAnimations) { + // when finished, set this to 0 to signify not animating + mAnimateDayHeight = 0 + mUseExpandIcon = !mShowAllAllDayEvents + } + mRemeasure = true + invalidate() } - mRemeasure = true; - invalidate(); - } - }); - return animator; - } + }) + return animator + } // setter for the 'box +n' alpha text used by the animator - public void setMoreAllDayEventsTextAlpha(int alpha) { - mMoreAlldayEventsTextAlpha = alpha; - invalidate(); + fun setMoreAllDayEventsTextAlpha(alpha: Int) { + mMoreAlldayEventsTextAlpha = alpha + invalidate() } // setter for the height of the allday area used by the animator - public void setAnimateDayHeight(int height) { - mAnimateDayHeight = height; - mRemeasure = true; - invalidate(); + fun setAnimateDayHeight(height: Int) { + mAnimateDayHeight = height + mRemeasure = true + invalidate() } // setter for the height of allday events used by the animator - public void setAnimateDayEventHeight(int height) { - mAnimateDayEventHeight = height; - mRemeasure = true; - invalidate(); + fun setAnimateDayEventHeight(height: Int) { + mAnimateDayEventHeight = height + mRemeasure = true + invalidate() } - private void doSingleTapUp(MotionEvent ev) { + private fun doSingleTapUp(ev: MotionEvent) { if (!mHandleActionUp || mScrolling) { - return; + return } - - int x = (int) ev.getX(); - int y = (int) ev.getY(); - int selectedDay = mSelectionDay; - int selectedHour = mSelectionHour; - + val x = ev.getX() as Int + val y = ev.getY() as Int + val selectedDay = mSelectionDay + val selectedHour = mSelectionHour if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { // check if the tap was in the allday expansion area - int bottom = mFirstCell; - if((x < mHoursWidth && y > DAY_HEADER_HEIGHT && y < DAY_HEADER_HEIGHT + mAlldayHeight) - || (!mShowAllAllDayEvents && mAnimateDayHeight == 0 && y < bottom && - y >= bottom - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)) { - doExpandAllDayClick(); - return; + val bottom = mFirstCell + if (x < mHoursWidth && y > DAY_HEADER_HEIGHT && y < DAY_HEADER_HEIGHT + mAlldayHeight + || !mShowAllAllDayEvents && mAnimateDayHeight == 0 && y < bottom && y >= bottom - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT + ) { + doExpandAllDayClick() + return } } - - boolean validPosition = setSelectionFromPosition(x, y, false); + val validPosition = setSelectionFromPosition(x, y, false) if (!validPosition) { if (y < DAY_HEADER_HEIGHT) { - Time selectedTime = new Time(mBaseDate); - selectedTime.setJulianDay(mSelectionDay); - selectedTime.hour = mSelectionHour; - selectedTime.normalize(true /* ignore isDst */); - mController.sendEvent(this, EventType.GO_TO, null, null, selectedTime, -1, - ViewType.DAY, CalendarController.EXTRA_GOTO_DATE, null, null); - } - return; - } - - boolean hasSelection = mSelectionMode != SELECTION_HIDDEN; - boolean pressedSelected = (hasSelection || mTouchExplorationEnabled) - && selectedDay == mSelectionDay && selectedHour == mSelectionHour; - + val selectedTime = Time(mBaseDate) + selectedTime.setJulianDay(mSelectionDay) + selectedTime.hour = mSelectionHour + selectedTime.normalize(true /* ignore isDst */) + mController.sendEvent( + this, EventType.GO_TO, null, null, selectedTime, -1, + ViewType.DAY, CalendarController.EXTRA_GOTO_DATE, null, null + ) + } + return + } + val hasSelection = mSelectionMode != SELECTION_HIDDEN + val pressedSelected = ((hasSelection || mTouchExplorationEnabled) + && selectedDay == mSelectionDay && selectedHour == mSelectionHour) if (mSelectedEvent != null) { // If the tap is on an event, launch the "View event" view if (mIsAccessibilityEnabled) { - mAccessibilityMgr.interrupt(); + mAccessibilityMgr.interrupt() } - - mSelectionMode = SELECTION_HIDDEN; - - int yLocation = - (int)((mSelectedEvent.top + mSelectedEvent.bottom)/2); + mSelectionMode = SELECTION_HIDDEN + var yLocation = ((mSelectedEvent.top + mSelectedEvent.bottom) / 2) as Int // Y location is affected by the position of the event in the scrolling // view (mViewStartY) and the presence of all day events (mFirstCell) if (!mSelectedEvent.allDay) { - yLocation += (mFirstCell - mViewStartY); + yLocation += mFirstCell - mViewStartY } - mClickedYLocation = yLocation; - long clearDelay = (CLICK_DISPLAY_DURATION + mOnDownDelay) - - (System.currentTimeMillis() - mDownTouchTime); + mClickedYLocation = yLocation + val clearDelay: Long = CLICK_DISPLAY_DURATION + mOnDownDelay - + (System.currentTimeMillis() - mDownTouchTime) if (clearDelay > 0) { - this.postDelayed(mClearClick, clearDelay); + this.postDelayed(mClearClick, clearDelay) } else { - this.post(mClearClick); + this.post(mClearClick) } } - invalidate(); + invalidate() } - private void doLongPress(MotionEvent ev) { - eventClickCleanup(); + private fun doLongPress(ev: MotionEvent) { + eventClickCleanup() if (mScrolling) { - return; + return } // Scale gesture in progress - if (mStartingSpanY != 0) { - return; + if (mStartingSpanY != 0f) { + return } - - int x = (int) ev.getX(); - int y = (int) ev.getY(); - - boolean validPosition = setSelectionFromPosition(x, y, false); + val x = ev.getX() as Int + val y = ev.getY() as Int + val validPosition = setSelectionFromPosition(x, y, false) if (!validPosition) { // return if the touch wasn't on an area of concern - return; + return } - - invalidate(); - performLongClick(); + invalidate() + performLongClick() } - private void doScroll(MotionEvent e1, MotionEvent e2, float deltaX, float deltaY) { - cancelAnimation(); + private fun doScroll(e1: MotionEvent, e2: MotionEvent, deltaX: Float, deltaY: Float) { + cancelAnimation() if (mStartingScroll) { - mInitialScrollX = 0; - mInitialScrollY = 0; - mStartingScroll = false; - } - - mInitialScrollX += deltaX; - mInitialScrollY += deltaY; - int distanceX = (int) mInitialScrollX; - int distanceY = (int) mInitialScrollY; - - final float focusY = getAverageY(e2); + mInitialScrollX = 0f + mInitialScrollY = 0f + mStartingScroll = false + } + mInitialScrollX += deltaX + mInitialScrollY += deltaY + val distanceX = mInitialScrollX.toInt() + val distanceY = mInitialScrollY.toInt() + val focusY = getAverageY(e2) if (mRecalCenterHour) { // Calculate the hour that correspond to the average of the Y touch points - mGestureCenterHour = (mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) - / (mCellHeight + DAY_GAP); - mRecalCenterHour = false; + mGestureCenterHour = ((mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) + / (mCellHeight + DAY_GAP)) + mRecalCenterHour = false } // If we haven't figured out the predominant scroll direction yet, // then do it now. if (mTouchMode == TOUCH_MODE_DOWN) { - int absDistanceX = Math.abs(distanceX); - int absDistanceY = Math.abs(distanceY); - mScrollStartY = mViewStartY; - mPreviousDirection = 0; - + val absDistanceX: Int = Math.abs(distanceX) + val absDistanceY: Int = Math.abs(distanceY) + mScrollStartY = mViewStartY + mPreviousDirection = 0 if (absDistanceX > absDistanceY) { - int slopFactor = mScaleGestureDetector.isInProgress() ? 20 : 2; + val slopFactor = if (mScaleGestureDetector.isInProgress()) 20 else 2 if (absDistanceX > mScaledPagingTouchSlop * slopFactor) { - mTouchMode = TOUCH_MODE_HSCROLL; - mViewStartX = distanceX; - initNextView(-mViewStartX); + mTouchMode = TOUCH_MODE_HSCROLL + mViewStartX = distanceX + initNextView(-mViewStartX) } } else { - mTouchMode = TOUCH_MODE_VSCROLL; + mTouchMode = TOUCH_MODE_VSCROLL } - } else if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { + } else if (mTouchMode and TOUCH_MODE_HSCROLL != 0) { // We are already scrolling horizontally, so check if we // changed the direction of scrolling so that the other week // is now visible. - mViewStartX = distanceX; + mViewStartX = distanceX if (distanceX != 0) { - int direction = (distanceX > 0) ? 1 : -1; + val direction = if (distanceX > 0) 1 else -1 if (direction != mPreviousDirection) { // The user has switched the direction of scrolling // so re-init the next view - initNextView(-mViewStartX); - mPreviousDirection = direction; + initNextView(-mViewStartX) + mPreviousDirection = direction } } } - - if ((mTouchMode & TOUCH_MODE_VSCROLL) != 0) { + if (mTouchMode and TOUCH_MODE_VSCROLL != 0) { // Calculate the top of the visible region in the calendar grid. // Increasing/decrease this will scroll the calendar grid up/down. - mViewStartY = (int) ((mGestureCenterHour * (mCellHeight + DAY_GAP)) - - focusY + DAY_HEADER_HEIGHT + mAlldayHeight); + mViewStartY = ((mGestureCenterHour * (mCellHeight + DAY_GAP) + - focusY) + DAY_HEADER_HEIGHT + mAlldayHeight).toInt() // If dragging while already at the end, do a glow - final int pulledToY = (int) (mScrollStartY + deltaY); + val pulledToY = (mScrollStartY + deltaY).toInt() if (pulledToY < 0) { - mEdgeEffectTop.onPull(deltaY / mViewHeight); + mEdgeEffectTop.onPull(deltaY / mViewHeight) if (!mEdgeEffectBottom.isFinished()) { - mEdgeEffectBottom.onRelease(); + mEdgeEffectBottom.onRelease() } } else if (pulledToY > mMaxViewStartY) { - mEdgeEffectBottom.onPull(deltaY / mViewHeight); + mEdgeEffectBottom.onPull(deltaY / mViewHeight) if (!mEdgeEffectTop.isFinished()) { - mEdgeEffectTop.onRelease(); + mEdgeEffectTop.onRelease() } } - if (mViewStartY < 0) { - mViewStartY = 0; - mRecalCenterHour = true; + mViewStartY = 0 + mRecalCenterHour = true } else if (mViewStartY > mMaxViewStartY) { - mViewStartY = mMaxViewStartY; - mRecalCenterHour = true; + mViewStartY = mMaxViewStartY + mRecalCenterHour = true } if (mRecalCenterHour) { // Calculate the hour that correspond to the average of the Y touch points - mGestureCenterHour = (mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) - / (mCellHeight + DAY_GAP); - mRecalCenterHour = false; + mGestureCenterHour = ((mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) + / (mCellHeight + DAY_GAP)) + mRecalCenterHour = false } - computeFirstHour(); + computeFirstHour() } - - mScrolling = true; - - mSelectionMode = SELECTION_HIDDEN; - invalidate(); + mScrolling = true + mSelectionMode = SELECTION_HIDDEN + invalidate() } - private float getAverageY(MotionEvent me) { - int count = me.getPointerCount(); - float focusY = 0; - for (int i = 0; i < count; i++) { - focusY += me.getY(i); + private fun getAverageY(me: MotionEvent): Float { + val count: Int = me.getPointerCount() + var focusY = 0f + for (i in 0 until count) { + focusY += me.getY(i) } - focusY /= count; - return focusY; + focusY /= count.toFloat() + return focusY } - private void cancelAnimation() { - Animation in = mViewSwitcher.getInAnimation(); - if (in != null) { + private fun cancelAnimation() { + val `in`: Animation = mViewSwitcher.getInAnimation() + if (`in` != null) { // cancel() doesn't terminate cleanly. - in.scaleCurrentDuration(0); + `in`.scaleCurrentDuration(0) } - Animation out = mViewSwitcher.getOutAnimation(); + val out: Animation = mViewSwitcher.getOutAnimation() if (out != null) { // cancel() doesn't terminate cleanly. - out.scaleCurrentDuration(0); + out.scaleCurrentDuration(0) } } - private void doFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - cancelAnimation(); - - mSelectionMode = SELECTION_HIDDEN; - eventClickCleanup(); - - mOnFlingCalled = true; - - if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { + private fun doFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float) { + cancelAnimation() + mSelectionMode = SELECTION_HIDDEN + eventClickCleanup() + mOnFlingCalled = true + if (mTouchMode and TOUCH_MODE_HSCROLL != 0) { // Horizontal fling. // initNextView(deltaX); - mTouchMode = TOUCH_MODE_INITIAL_STATE; - if (DEBUG) Log.d(TAG, "doFling: velocityX " + velocityX); - int deltaX = (int) e2.getX() - (int) e1.getX(); - switchViews(deltaX < 0, mViewStartX, mViewWidth, velocityX); - mViewStartX = 0; - return; + mTouchMode = TOUCH_MODE_INITIAL_STATE + if (DEBUG) Log.d(TAG, "doFling: velocityX $velocityX") + val deltaX = e2.getX() as Int - e1.getX() as Int + switchViews(deltaX < 0, mViewStartX.toFloat(), mViewWidth.toFloat(), velocityX) + mViewStartX = 0 + return } - - if ((mTouchMode & TOUCH_MODE_VSCROLL) == 0) { - if (DEBUG) Log.d(TAG, "doFling: no fling"); - return; + if (mTouchMode and TOUCH_MODE_VSCROLL == 0) { + if (DEBUG) Log.d(TAG, "doFling: no fling") + return } // Vertical fling. - mTouchMode = TOUCH_MODE_INITIAL_STATE; - mViewStartX = 0; - + mTouchMode = TOUCH_MODE_INITIAL_STATE + mViewStartX = 0 if (DEBUG) { - Log.d(TAG, "doFling: mViewStartY" + mViewStartY + " velocityY " + velocityY); + Log.d(TAG, "doFling: mViewStartY$mViewStartY velocityY $velocityY") } // Continue scrolling vertically - mScrolling = true; - mScroller.fling(0 /* startX */, mViewStartY /* startY */, 0 /* velocityX */, - (int) -velocityY, 0 /* minX */, 0 /* maxX */, 0 /* minY */, - mMaxViewStartY /* maxY */, OVERFLING_DISTANCE, OVERFLING_DISTANCE); + mScrolling = true + mScroller.fling( + 0 /* startX */, mViewStartY /* startY */, 0 /* velocityX */, + (-velocityY).toInt(), 0 /* minX */, 0 /* maxX */, 0 /* minY */, + mMaxViewStartY /* maxY */, OVERFLING_DISTANCE, OVERFLING_DISTANCE + ) // When flinging down, show a glow when it hits the end only if it // wasn't started at the top if (velocityY > 0 && mViewStartY != 0) { - mCallEdgeEffectOnAbsorb = true; - } - // When flinging up, show a glow when it hits the end only if it wasn't - // started at the bottom - else if (velocityY < 0 && mViewStartY != mMaxViewStartY) { - mCallEdgeEffectOnAbsorb = true; + mCallEdgeEffectOnAbsorb = true + } else if (velocityY < 0 && mViewStartY != mMaxViewStartY) { + mCallEdgeEffectOnAbsorb = true } - mHandler.post(mContinueScroll); + mHandler.post(mContinueScroll) } - private boolean initNextView(int deltaX) { + private fun initNextView(deltaX: Int): Boolean { // Change the view to the previous day or week - DayView view = (DayView) mViewSwitcher.getNextView(); - Time date = view.mBaseDate; - date.set(mBaseDate); - boolean switchForward; + val view = mViewSwitcher.getNextView() as DayView + val date: Time? = view.mBaseDate + date.set(mBaseDate) + val switchForward: Boolean if (deltaX > 0) { - date.monthDay -= mNumDays; - view.setSelectedDay(mSelectionDay - mNumDays); - switchForward = false; + date.monthDay -= mNumDays + view.selectedDay = mSelectionDay - mNumDays + switchForward = false } else { - date.monthDay += mNumDays; - view.setSelectedDay(mSelectionDay + mNumDays); - switchForward = true; + date.monthDay += mNumDays + view.selectedDay = mSelectionDay + mNumDays + switchForward = true } - date.normalize(true /* ignore isDst */); - initView(view); - view.layout(getLeft(), getTop(), getRight(), getBottom()); - view.reloadEvents(); - return switchForward; + date.normalize(true /* ignore isDst */) + initView(view) + view.layout(getLeft(), getTop(), getRight(), getBottom()) + view.reloadEvents() + return switchForward } // ScaleGestureDetector.OnScaleGestureListener - public boolean onScaleBegin(ScaleGestureDetector detector) { - mHandleActionUp = false; - float gestureCenterInPixels = detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight; - mGestureCenterHour = (mViewStartY + gestureCenterInPixels) / (mCellHeight + DAY_GAP); - - mStartingSpanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY())); - mCellHeightBeforeScaleGesture = mCellHeight; - + fun onScaleBegin(detector: ScaleGestureDetector): Boolean { + mHandleActionUp = false + val gestureCenterInPixels: Float = detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight + mGestureCenterHour = (mViewStartY + gestureCenterInPixels) / (mCellHeight + DAY_GAP) + mStartingSpanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY())) + mCellHeightBeforeScaleGesture = mCellHeight if (DEBUG_SCALING) { - float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP); - Log.d(TAG, "onScaleBegin: mGestureCenterHour:" + mGestureCenterHour - + "\tViewStartHour: " + ViewStartHour + "\tmViewStartY:" + mViewStartY - + "\tmCellHeight:" + mCellHeight + " SpanY:" + detector.getCurrentSpanY()); + val ViewStartHour = mViewStartY / (mCellHeight + DAY_GAP).toFloat() + Log.d( + TAG, "onScaleBegin: mGestureCenterHour:" + mGestureCenterHour + + "\tViewStartHour: " + ViewStartHour + "\tmViewStartY:" + mViewStartY + + "\tmCellHeight:" + mCellHeight + " SpanY:" + detector.getCurrentSpanY() + ) } - - return true; + return true } // ScaleGestureDetector.OnScaleGestureListener - public boolean onScale(ScaleGestureDetector detector) { - float spanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY())); - - mCellHeight = (int) (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY); - + fun onScale(detector: ScaleGestureDetector): Boolean { + val spanY: Float = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY())) + mCellHeight = (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY).toInt() if (mCellHeight < mMinCellHeight) { // If mStartingSpanY is too small, even a small increase in the // gesture can bump the mCellHeight beyond MAX_CELL_HEIGHT - mStartingSpanY = spanY; - mCellHeight = mMinCellHeight; - mCellHeightBeforeScaleGesture = mMinCellHeight; + mStartingSpanY = spanY + mCellHeight = mMinCellHeight + mCellHeightBeforeScaleGesture = mMinCellHeight } else if (mCellHeight > MAX_CELL_HEIGHT) { - mStartingSpanY = spanY; - mCellHeight = MAX_CELL_HEIGHT; - mCellHeightBeforeScaleGesture = MAX_CELL_HEIGHT; + mStartingSpanY = spanY + mCellHeight = MAX_CELL_HEIGHT + mCellHeightBeforeScaleGesture = MAX_CELL_HEIGHT } - - int gestureCenterInPixels = (int) detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight; - mViewStartY = (int) (mGestureCenterHour * (mCellHeight + DAY_GAP)) - gestureCenterInPixels; - mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight; - + val gestureCenterInPixels = detector.getFocusY() as Int - DAY_HEADER_HEIGHT - mAlldayHeight + mViewStartY = (mGestureCenterHour * (mCellHeight + DAY_GAP)).toInt() - gestureCenterInPixels + mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight if (DEBUG_SCALING) { - float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP); - Log.d(TAG, "onScale: mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: " - + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:" - + mCellHeight + " SpanY:" + detector.getCurrentSpanY()); + val ViewStartHour = mViewStartY / (mCellHeight + DAY_GAP).toFloat() + Log.d( + TAG, "onScale: mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: " + + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:" + + mCellHeight + " SpanY:" + detector.getCurrentSpanY() + ) } - if (mViewStartY < 0) { - mViewStartY = 0; - mGestureCenterHour = (mViewStartY + gestureCenterInPixels) - / (float) (mCellHeight + DAY_GAP); + mViewStartY = 0 + mGestureCenterHour = ((mViewStartY + gestureCenterInPixels) + / (mCellHeight + DAY_GAP).toFloat()) } else if (mViewStartY > mMaxViewStartY) { - mViewStartY = mMaxViewStartY; - mGestureCenterHour = (mViewStartY + gestureCenterInPixels) - / (float) (mCellHeight + DAY_GAP); + mViewStartY = mMaxViewStartY + mGestureCenterHour = ((mViewStartY + gestureCenterInPixels) + / (mCellHeight + DAY_GAP).toFloat()) } - computeFirstHour(); - - mRemeasure = true; - invalidate(); - return true; + computeFirstHour() + mRemeasure = true + invalidate() + return true } // ScaleGestureDetector.OnScaleGestureListener - public void onScaleEnd(ScaleGestureDetector detector) { - mScrollStartY = mViewStartY; - mInitialScrollY = 0; - mInitialScrollX = 0; - mStartingSpanY = 0; + fun onScaleEnd(detector: ScaleGestureDetector?) { + mScrollStartY = mViewStartY + mInitialScrollY = 0f + mInitialScrollX = 0f + mStartingSpanY = 0f } @Override - public boolean onTouchEvent(MotionEvent ev) { - int action = ev.getAction(); - if (DEBUG) Log.e(TAG, "" + action + " ev.getPointerCount() = " + ev.getPointerCount()); - - if ((ev.getActionMasked() == MotionEvent.ACTION_DOWN) || - (ev.getActionMasked() == MotionEvent.ACTION_UP) || - (ev.getActionMasked() == MotionEvent.ACTION_POINTER_UP) || - (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN)) { - mRecalCenterHour = true; - } - - if ((mTouchMode & TOUCH_MODE_HSCROLL) == 0) { - mScaleGestureDetector.onTouchEvent(ev); - } - - switch (action) { - case MotionEvent.ACTION_DOWN: - mStartingScroll = true; + fun onTouchEvent(ev: MotionEvent): Boolean { + val action: Int = ev.getAction() + if (DEBUG) Log.e(TAG, "" + action + " ev.getPointerCount() = " + ev.getPointerCount()) + if (ev.getActionMasked() === MotionEvent.ACTION_DOWN || + ev.getActionMasked() === MotionEvent.ACTION_UP || + ev.getActionMasked() === MotionEvent.ACTION_POINTER_UP || + ev.getActionMasked() === MotionEvent.ACTION_POINTER_DOWN + ) { + mRecalCenterHour = true + } + if (mTouchMode and TOUCH_MODE_HSCROLL == 0) { + mScaleGestureDetector.onTouchEvent(ev) + } + return when (action) { + MotionEvent.ACTION_DOWN -> { + mStartingScroll = true if (DEBUG) { - Log.e(TAG, "ACTION_DOWN ev.getDownTime = " + ev.getDownTime() + " Cnt=" - + ev.getPointerCount()); + Log.e( + TAG, + "ACTION_DOWN ev.getDownTime = " + ev.getDownTime().toString() + " Cnt=" + + ev.getPointerCount() + ) } - - int bottom = mAlldayHeight + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN; - if (ev.getY() < bottom) { - mTouchStartedInAlldayArea = true; + val bottom = + mAlldayHeight + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN + mTouchStartedInAlldayArea = if (ev.getY() < bottom) { + true } else { - mTouchStartedInAlldayArea = false; + false } - mHandleActionUp = true; - mGestureDetector.onTouchEvent(ev); - return true; - - case MotionEvent.ACTION_MOVE: - if (DEBUG) Log.e(TAG, "ACTION_MOVE Cnt=" + ev.getPointerCount() + DayView.this); - mGestureDetector.onTouchEvent(ev); - return true; - - case MotionEvent.ACTION_UP: - if (DEBUG) Log.e(TAG, "ACTION_UP Cnt=" + ev.getPointerCount() + mHandleActionUp); - mEdgeEffectTop.onRelease(); - mEdgeEffectBottom.onRelease(); - mStartingScroll = false; - mGestureDetector.onTouchEvent(ev); + mHandleActionUp = true + mGestureDetector.onTouchEvent(ev) + true + } + MotionEvent.ACTION_MOVE -> { + if (DEBUG) Log.e( + TAG, + "ACTION_MOVE Cnt=" + ev.getPointerCount() + this@DayView + ) + mGestureDetector.onTouchEvent(ev) + true + } + MotionEvent.ACTION_UP -> { + if (DEBUG) Log.e( + TAG, + "ACTION_UP Cnt=" + ev.getPointerCount() + mHandleActionUp + ) + mEdgeEffectTop.onRelease() + mEdgeEffectBottom.onRelease() + mStartingScroll = false + mGestureDetector.onTouchEvent(ev) if (!mHandleActionUp) { - mHandleActionUp = true; - mViewStartX = 0; - invalidate(); - return true; + mHandleActionUp = true + mViewStartX = 0 + invalidate() + return true } - if (mOnFlingCalled) { - return true; + return true } // If we were scrolling, then reset the selected hour so that it // is visible. if (mScrolling) { - mScrolling = false; - resetSelectedHour(); - invalidate(); + mScrolling = false + resetSelectedHour() + invalidate() } - - if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { - mTouchMode = TOUCH_MODE_INITIAL_STATE; + if (mTouchMode and TOUCH_MODE_HSCROLL != 0) { + mTouchMode = TOUCH_MODE_INITIAL_STATE if (Math.abs(mViewStartX) > mHorizontalSnapBackThreshold) { // The user has gone beyond the threshold so switch views - if (DEBUG) Log.d(TAG, "- horizontal scroll: switch views"); - switchViews(mViewStartX > 0, mViewStartX, mViewWidth, 0); - mViewStartX = 0; - return true; + if (DEBUG) Log.d( + TAG, + "- horizontal scroll: switch views" + ) + switchViews( + mViewStartX > 0, + mViewStartX.toFloat(), + mViewWidth.toFloat(), + 0f + ) + mViewStartX = 0 + return true } else { // Not beyond the threshold so invalidate which will cause // the view to snap back. Also call recalc() to ensure // that we have the correct starting date and title. - if (DEBUG) Log.d(TAG, "- horizontal scroll: snap back"); - recalc(); - invalidate(); - mViewStartX = 0; + if (DEBUG) Log.d( + TAG, + "- horizontal scroll: snap back" + ) + recalc() + invalidate() + mViewStartX = 0 } } - - return true; - - // This case isn't expected to happen. - case MotionEvent.ACTION_CANCEL: - if (DEBUG) Log.e(TAG, "ACTION_CANCEL"); - mGestureDetector.onTouchEvent(ev); - mScrolling = false; - resetSelectedHour(); - return true; - - default: - if (DEBUG) Log.e(TAG, "Not MotionEvent " + ev.toString()); + true + } + MotionEvent.ACTION_CANCEL -> { + if (DEBUG) Log.e( + TAG, + "ACTION_CANCEL" + ) + mGestureDetector.onTouchEvent(ev) + mScrolling = false + resetSelectedHour() + true + } + else -> { + if (DEBUG) Log.e( + TAG, + "Not MotionEvent " + ev.toString() + ) if (mGestureDetector.onTouchEvent(ev)) { - return true; - } - return super.onTouchEvent(ev); + true + } else super.onTouchEvent(ev) + } } } - public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) { - MenuItem item; + fun onCreateContextMenu(menu: ContextMenu, view: View?, menuInfo: ContextMenuInfo?) { + var item: MenuItem // If the trackball is held down, then the context menu pops up and // we never get onKeyUp() for the long-press. So check for it here // and change the selection to the long-press state. if (mSelectionMode != SELECTION_LONGPRESS) { - invalidate(); + invalidate() } - - final long startMillis = getSelectedTimeInMillis(); - int flags = DateUtils.FORMAT_SHOW_TIME - | DateUtils.FORMAT_CAP_NOON_MIDNIGHT - | DateUtils.FORMAT_SHOW_WEEKDAY; - final String title = Utils.formatDateRange(mContext, startMillis, startMillis, flags); - menu.setHeaderTitle(title); - - mPopup.dismiss(); + val startMillis = selectedTimeInMillis + val flags: Int = (DateUtils.FORMAT_SHOW_TIME + or DateUtils.FORMAT_CAP_NOON_MIDNIGHT + or DateUtils.FORMAT_SHOW_WEEKDAY) + val title: String = Utils.formatDateRange(mContext, startMillis, startMillis, flags) + menu.setHeaderTitle(title) + mPopup.dismiss() } /** @@ -3553,201 +3112,191 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * @param x the x position of the touch * @param y the y position of the touch * @param keepOldSelection - do not change the selection info (used for invoking accessibility - * messages) + * messages) * @return true if the touch position is valid */ - private boolean setSelectionFromPosition(int x, final int y, boolean keepOldSelection) { - - Event savedEvent = null; - int savedDay = 0; - int savedHour = 0; - boolean savedAllDay = false; + private fun setSelectionFromPosition(x: Int, y: Int, keepOldSelection: Boolean): Boolean { + var x = x + var savedEvent: Event? = null + var savedDay = 0 + var savedHour = 0 + var savedAllDay = false if (keepOldSelection) { // Store selection info and restore it at the end. This way, we can invoke the // right accessibility message without affecting the selection. - savedEvent = mSelectedEvent; - savedDay = mSelectionDay; - savedHour = mSelectionHour; - savedAllDay = mSelectionAllday; + savedEvent = mSelectedEvent + savedDay = mSelectionDay + savedHour = mSelectionHour + savedAllDay = mSelectionAllday } if (x < mHoursWidth) { - x = mHoursWidth; + x = mHoursWidth } - - int day = (x - mHoursWidth) / (mCellWidth + DAY_GAP); + var day = (x - mHoursWidth) / (mCellWidth + DAY_GAP) if (day >= mNumDays) { - day = mNumDays - 1; + day = mNumDays - 1 } - day += mFirstJulianDay; - setSelectedDay(day); - + day += mFirstJulianDay + selectedDay = day if (y < DAY_HEADER_HEIGHT) { - sendAccessibilityEventAsNeeded(false); - return false; + sendAccessibilityEventAsNeeded(false) + return false } - - setSelectedHour(mFirstHour); /* First fully visible hour */ - - if (y < mFirstCell) { - mSelectionAllday = true; + setSelectedHour(mFirstHour) /* First fully visible hour */ + mSelectionAllday = if (y < mFirstCell) { + true } else { // y is now offset from top of the scrollable region - int adjustedY = y - mFirstCell; - + val adjustedY = y - mFirstCell if (adjustedY < mFirstHourOffset) { - setSelectedHour(mSelectionHour - 1); /* In the partially visible hour */ + setSelectedHour(mSelectionHour - 1) /* In the partially visible hour */ } else { - setSelectedHour(mSelectionHour + - (adjustedY - mFirstHourOffset) / (mCellHeight + HOUR_GAP)); + setSelectedHour( + mSelectionHour + + (adjustedY - mFirstHourOffset) / (mCellHeight + HOUR_GAP) + ) } - - mSelectionAllday = false; + false } - - findSelectedEvent(x, y); - - sendAccessibilityEventAsNeeded(true); + findSelectedEvent(x, y) + sendAccessibilityEventAsNeeded(true) // Restore old values if (keepOldSelection) { - mSelectedEvent = savedEvent; - mSelectionDay = savedDay; - mSelectionHour = savedHour; - mSelectionAllday = savedAllDay; - } - return true; - } - - private void findSelectedEvent(int x, int y) { - int date = mSelectionDay; - int cellWidth = mCellWidth; - ArrayList events = mEvents; - int numEvents = events.size(); - int left = computeDayLeftPosition(mSelectionDay - mFirstJulianDay); - int top = 0; - setSelectedEvent(null); - - mSelectedEvents.clear(); + mSelectedEvent = savedEvent + mSelectionDay = savedDay + mSelectionHour = savedHour + mSelectionAllday = savedAllDay + } + return true + } + + private fun findSelectedEvent(x: Int, y: Int) { + var y = y + val date = mSelectionDay + val cellWidth = mCellWidth + var events: ArrayList? = mEvents + var numEvents: Int = events.size() + val left = computeDayLeftPosition(mSelectionDay - mFirstJulianDay) + val top = 0 + setSelectedEvent(null) + mSelectedEvents.clear() if (mSelectionAllday) { - float yDistance; - float minYdistance = 10000.0f; // any large number - Event closestEvent = null; - float drawHeight = mAlldayHeight; - int yOffset = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN; - int maxUnexpandedColumn = mMaxUnexpandedAlldayEventCount; + var yDistance: Float + var minYdistance = 10000.0f // any large number + var closestEvent: Event? = null + val drawHeight = mAlldayHeight.toFloat() + val yOffset = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN + var maxUnexpandedColumn = mMaxUnexpandedAlldayEventCount if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { // Leave a gap for the 'box +n' text - maxUnexpandedColumn--; + maxUnexpandedColumn-- } - events = mAllDayEvents; - numEvents = events.size(); - for (int i = 0; i < numEvents; i++) { - Event event = events.get(i); + events = mAllDayEvents + numEvents = events.size() + for (i in 0 until numEvents) { + val event: Event = events.get(i) if (!event.drawAsAllday() || - (!mShowAllAllDayEvents && event.getColumn() >= maxUnexpandedColumn)) { + !mShowAllAllDayEvents && event.getColumn() >= maxUnexpandedColumn + ) { // Don't check non-allday events or events that aren't shown - continue; + continue } - if (event.startDay <= mSelectionDay && event.endDay >= mSelectionDay) { - float numRectangles = mShowAllAllDayEvents ? mMaxAlldayEvents - : mMaxUnexpandedAlldayEventCount; - float height = drawHeight / numRectangles; + val numRectangles = + if (mShowAllAllDayEvents) mMaxAlldayEvents.toFloat() else mMaxUnexpandedAlldayEventCount.toFloat() + var height = drawHeight / numRectangles if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) { - height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; + height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT.toFloat() } - float eventTop = yOffset + height * event.getColumn(); - float eventBottom = eventTop + height; + val eventTop: Float = yOffset + height * event.getColumn() + val eventBottom = eventTop + height if (eventTop < y && eventBottom > y) { // If the touch is inside the event rectangle, then // add the event. - mSelectedEvents.add(event); - closestEvent = event; - break; + mSelectedEvents.add(event) + closestEvent = event + break } else { // Find the closest event - if (eventTop >= y) { - yDistance = eventTop - y; + yDistance = if (eventTop >= y) { + eventTop - y } else { - yDistance = y - eventBottom; + y - eventBottom } if (yDistance < minYdistance) { - minYdistance = yDistance; - closestEvent = event; + minYdistance = yDistance + closestEvent = event } } } } - setSelectedEvent(closestEvent); - return; + setSelectedEvent(closestEvent) + return } // Adjust y for the scrollable bitmap - y += mViewStartY - mFirstCell; + y += mViewStartY - mFirstCell // Use a region around (x,y) for the selection region - Rect region = mRect; - region.left = x - 10; - region.right = x + 10; - region.top = y - 10; - region.bottom = y + 10; - - EventGeometry geometry = mEventGeometry; - - for (int i = 0; i < numEvents; i++) { - Event event = events.get(i); + val region: Rect = mRect + region.left = x - 10 + region.right = x + 10 + region.top = y - 10 + region.bottom = y + 10 + val geometry: EventGeometry = mEventGeometry + for (i in 0 until numEvents) { + val event: Event = events.get(i) // Compute the event rectangle. if (!geometry.computeEventRect(date, left, top, cellWidth, event)) { - continue; + continue } // If the event intersects the selection region, then add it to // mSelectedEvents. if (geometry.eventIntersectsSelection(event, region)) { - mSelectedEvents.add(event); + mSelectedEvents.add(event) } } // If there are any events in the selected region, then assign the // closest one to mSelectedEvent. if (mSelectedEvents.size() > 0) { - int len = mSelectedEvents.size(); - Event closestEvent = null; - float minDist = mViewWidth + mViewHeight; // some large distance - for (int index = 0; index < len; index++) { - Event ev = mSelectedEvents.get(index); - float dist = geometry.pointToEvent(x, y, ev); + val len: Int = mSelectedEvents.size() + var closestEvent: Event? = null + var minDist = (mViewWidth + mViewHeight).toFloat() // some large distance + for (index in 0 until len) { + val ev: Event = mSelectedEvents.get(index) + val dist: Float = geometry.pointToEvent(x, y, ev) if (dist < minDist) { - minDist = dist; - closestEvent = ev; + minDist = dist + closestEvent = ev } } - setSelectedEvent(closestEvent); + setSelectedEvent(closestEvent) // Keep the selected hour and day consistent with the selected // event. They could be different if we touched on an empty hour // slot very close to an event in the previous hour slot. In // that case we will select the nearby event. - int startDay = mSelectedEvent.startDay; - int endDay = mSelectedEvent.endDay; + val startDay: Int = mSelectedEvent.startDay + val endDay: Int = mSelectedEvent.endDay if (mSelectionDay < startDay) { - setSelectedDay(startDay); + selectedDay = startDay } else if (mSelectionDay > endDay) { - setSelectedDay(endDay); + selectedDay = endDay } - - int startHour = mSelectedEvent.startTime / 60; - int endHour; - if (mSelectedEvent.startTime < mSelectedEvent.endTime) { - endHour = (mSelectedEvent.endTime - 1) / 60; + val startHour: Int = mSelectedEvent.startTime / 60 + val endHour: Int + endHour = if (mSelectedEvent.startTime < mSelectedEvent.endTime) { + (mSelectedEvent.endTime - 1) / 60 } else { - endHour = mSelectedEvent.endTime / 60; + mSelectedEvent.endTime / 60 } - if (mSelectionHour < startHour && mSelectionDay == startDay) { - setSelectedHour(startHour); + setSelectedHour(startHour) } else if (mSelectionHour > endHour && mSelectionDay == endDay) { - setSelectedHour(endHour); + setSelectedHour(endHour) } } } @@ -3755,213 +3304,204 @@ public class DayView extends View implements View.OnCreateContextMenuListener, // Encapsulates the code to continue the scrolling after the // finger is lifted. Instead of stopping the scroll immediately, // the scroll continues to "free spin" and gradually slows down. - private class ContinueScroll implements Runnable { - - public void run() { - mScrolling = mScrolling && mScroller.computeScrollOffset(); + private inner class ContinueScroll : Runnable { + fun run() { + mScrolling = mScrolling && mScroller.computeScrollOffset() if (!mScrolling || mPaused) { - resetSelectedHour(); - invalidate(); - return; + resetSelectedHour() + invalidate() + return } - - mViewStartY = mScroller.getCurrY(); - + mViewStartY = mScroller.getCurrY() if (mCallEdgeEffectOnAbsorb) { if (mViewStartY < 0) { - mEdgeEffectTop.onAbsorb((int) mLastVelocity); - mCallEdgeEffectOnAbsorb = false; + mEdgeEffectTop.onAbsorb(mLastVelocity.toInt()) + mCallEdgeEffectOnAbsorb = false } else if (mViewStartY > mMaxViewStartY) { - mEdgeEffectBottom.onAbsorb((int) mLastVelocity); - mCallEdgeEffectOnAbsorb = false; + mEdgeEffectBottom.onAbsorb(mLastVelocity.toInt()) + mCallEdgeEffectOnAbsorb = false } - mLastVelocity = mScroller.getCurrVelocity(); + mLastVelocity = mScroller.getCurrVelocity() } - if (mScrollStartY == 0 || mScrollStartY == mMaxViewStartY) { // Allow overscroll/springback only on a fling, // not a pull/fling from the end if (mViewStartY < 0) { - mViewStartY = 0; + mViewStartY = 0 } else if (mViewStartY > mMaxViewStartY) { - mViewStartY = mMaxViewStartY; + mViewStartY = mMaxViewStartY } } - - computeFirstHour(); - mHandler.post(this); - invalidate(); + computeFirstHour() + mHandler.post(this) + invalidate() } } /** * Cleanup the pop-up and timers. */ - public void cleanup() { + fun cleanup() { // Protect against null-pointer exceptions if (mPopup != null) { - mPopup.dismiss(); + mPopup.dismiss() } - mPaused = true; - mLastPopupEventID = INVALID_EVENT_ID; + mPaused = true + mLastPopupEventID = INVALID_EVENT_ID if (mHandler != null) { - mHandler.removeCallbacks(mDismissPopup); - mHandler.removeCallbacks(mUpdateCurrentTime); + mHandler.removeCallbacks(mDismissPopup) + mHandler.removeCallbacks(mUpdateCurrentTime) } - - Utils.setSharedPreference(mContext, GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, - mCellHeight); + Utils.setSharedPreference( + mContext, GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, + mCellHeight + ) // Clear all click animations - eventClickCleanup(); + eventClickCleanup() // Turn off redraw - mRemeasure = false; + mRemeasure = false // Turn off scrolling to make sure the view is in the correct state if we fling back to it - mScrolling = false; + mScrolling = false } - private void eventClickCleanup() { - this.removeCallbacks(mClearClick); - this.removeCallbacks(mSetClick); - mClickedEvent = null; - mSavedClickedEvent = null; + private fun eventClickCleanup() { + this.removeCallbacks(mClearClick) + this.removeCallbacks(mSetClick) + mClickedEvent = null + mSavedClickedEvent = null } - private void setSelectedEvent(Event e) { - mSelectedEvent = e; - mSelectedEventForAccessibility = e; + private fun setSelectedEvent(e: Event?) { + mSelectedEvent = e + mSelectedEventForAccessibility = e } - private void setSelectedHour(int h) { - mSelectionHour = h; - mSelectionHourForAccessibility = h; - } - private void setSelectedDay(int d) { - mSelectionDay = d; - mSelectionDayForAccessibility = d; + private fun setSelectedHour(h: Int) { + mSelectionHour = h + mSelectionHourForAccessibility = h } /** * Restart the update timer */ - public void restartCurrentTimeUpdates() { - mPaused = false; + fun restartCurrentTimeUpdates() { + mPaused = false if (mHandler != null) { - mHandler.removeCallbacks(mUpdateCurrentTime); - mHandler.post(mUpdateCurrentTime); + mHandler.removeCallbacks(mUpdateCurrentTime) + mHandler.post(mUpdateCurrentTime) } } @Override - protected void onDetachedFromWindow() { - cleanup(); - super.onDetachedFromWindow(); + protected fun onDetachedFromWindow() { + cleanup() + super.onDetachedFromWindow() } - class DismissPopup implements Runnable { - - public void run() { + internal inner class DismissPopup : Runnable { + fun run() { // Protect against null-pointer exceptions if (mPopup != null) { - mPopup.dismiss(); + mPopup.dismiss() } } } - class UpdateCurrentTime implements Runnable { - - public void run() { - long currentTime = System.currentTimeMillis(); - mCurrentTime.set(currentTime); + internal inner class UpdateCurrentTime : Runnable { + fun run() { + val currentTime: Long = System.currentTimeMillis() + mCurrentTime.set(currentTime) //% causes update to occur on 5 minute marks (11:10, 11:15, 11:20, etc.) - if (!DayView.this.mPaused) { - mHandler.postDelayed(mUpdateCurrentTime, UPDATE_CURRENT_TIME_DELAY - - (currentTime % UPDATE_CURRENT_TIME_DELAY)); + if (!mPaused) { + mHandler.postDelayed( + mUpdateCurrentTime, UPDATE_CURRENT_TIME_DELAY + - currentTime % UPDATE_CURRENT_TIME_DELAY + ) } - mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff); - invalidate(); + mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff) + invalidate() } } - class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { + internal inner class CalendarGestureListener : GestureDetector.SimpleOnGestureListener() { @Override - public boolean onSingleTapUp(MotionEvent ev) { - if (DEBUG) Log.e(TAG, "GestureDetector.onSingleTapUp"); - DayView.this.doSingleTapUp(ev); - return true; + fun onSingleTapUp(ev: MotionEvent): Boolean { + if (DEBUG) Log.e(TAG, "GestureDetector.onSingleTapUp") + doSingleTapUp(ev) + return true } @Override - public void onLongPress(MotionEvent ev) { - if (DEBUG) Log.e(TAG, "GestureDetector.onLongPress"); - DayView.this.doLongPress(ev); + fun onLongPress(ev: MotionEvent) { + if (DEBUG) Log.e(TAG, "GestureDetector.onLongPress") + doLongPress(ev) } @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (DEBUG) Log.e(TAG, "GestureDetector.onScroll"); - eventClickCleanup(); + fun onScroll( + e1: MotionEvent, + e2: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + var distanceY = distanceY + if (DEBUG) Log.e(TAG, "GestureDetector.onScroll") + eventClickCleanup() if (mTouchStartedInAlldayArea) { if (Math.abs(distanceX) < Math.abs(distanceY)) { // Make sure that click feedback is gone when you scroll from the // all day area - invalidate(); - return false; + invalidate() + return false } // don't scroll vertically if this started in the allday area - distanceY = 0; + distanceY = 0f } - DayView.this.doScroll(e1, e2, distanceX, distanceY); - return true; + doScroll(e1, e2, distanceX, distanceY) + return true } @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if (DEBUG) Log.e(TAG, "GestureDetector.onFling"); - + fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { + var velocityY = velocityY + if (DEBUG) Log.e(TAG, "GestureDetector.onFling") if (mTouchStartedInAlldayArea) { if (Math.abs(velocityX) < Math.abs(velocityY)) { - return false; + return false } // don't fling vertically if this started in the allday area - velocityY = 0; + velocityY = 0f } - DayView.this.doFling(e1, e2, velocityX, velocityY); - return true; + doFling(e1, e2, velocityX, velocityY) + return true } @Override - public boolean onDown(MotionEvent ev) { - if (DEBUG) Log.e(TAG, "GestureDetector.onDown"); - DayView.this.doDown(ev); - return true; + fun onDown(ev: MotionEvent): Boolean { + if (DEBUG) Log.e(TAG, "GestureDetector.onDown") + doDown(ev) + return true } } @Override - public boolean onLongClick(View v) { - return true; + fun onLongClick(v: View?): Boolean { + return true } - // The rest of this file was borrowed from Launcher2 - PagedView.java - private static final int MINIMUM_SNAP_VELOCITY = 2200; - - private class ScrollInterpolator implements Interpolator { - public ScrollInterpolator() { - } - - public float getInterpolation(float t) { - t -= 1.0f; - t = t * t * t * t * t + 1; - + private inner class ScrollInterpolator : Interpolator { + fun getInterpolation(t: Float): Float { + var t = t + t -= 1.0f + t = t * t * t * t * t + 1 if ((1 - t) * mAnimationDistance < 1) { - cancelAnimation(); + cancelAnimation() } - - return t; + return t } } - private long calculateDuration(float delta, float width, float velocity) { + private fun calculateDuration(delta: Float, width: Float, velocity: Float): Long { /* * Here we compute a "distance" that will be used in the computation of * the overall snap duration. This is a function of the actual distance @@ -3969,13 +3509,13 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * size in order to reduce the variance in snap duration as a function * of the distance the page needs to travel. */ - final float halfScreenSize = width / 2; - float distanceRatio = delta / width; - float distanceInfluenceForSnapDuration = distanceInfluenceForSnapDuration(distanceRatio); - float distance = halfScreenSize + halfScreenSize * distanceInfluenceForSnapDuration; - - velocity = Math.abs(velocity); - velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity); + var velocity = velocity + val halfScreenSize = width / 2 + val distanceRatio = delta / width + val distanceInfluenceForSnapDuration = distanceInfluenceForSnapDuration(distanceRatio) + val distance = halfScreenSize + halfScreenSize * distanceInfluenceForSnapDuration + velocity = Math.abs(velocity) + velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity) /* * we want the page's snap velocity to approximately match the velocity @@ -3983,14 +3523,16 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * the derivative of the scroll interpolator at zero, ie. 5. We use 6 to * make it a little slower. */ - long duration = 6 * Math.round(1000 * Math.abs(distance / velocity)); + val duration: Long = 6 * Math.round(1000 * Math.abs(distance / velocity)) if (DEBUG) { - Log.e(TAG, "halfScreenSize:" + halfScreenSize + " delta:" + delta + " distanceRatio:" - + distanceRatio + " distance:" + distance + " velocity:" + velocity - + " duration:" + duration + " distanceInfluenceForSnapDuration:" - + distanceInfluenceForSnapDuration); + Log.e( + TAG, "halfScreenSize:" + halfScreenSize + " delta:" + delta + " distanceRatio:" + + distanceRatio + " distance:" + distance + " velocity:" + velocity + + " duration:" + duration + " distanceInfluenceForSnapDuration:" + + distanceInfluenceForSnapDuration + ) } - return duration; + return duration } /* @@ -4000,9 +3542,355 @@ public class DayView extends View implements View.OnCreateContextMenuListener, * method to moderate the effect that the distance of travel has on the * overall snap duration. */ - private float distanceInfluenceForSnapDuration(float f) { - f -= 0.5f; // center the values about 0. - f *= 0.3f * Math.PI / 2.0f; - return (float) Math.sin(f); - } -} + private fun distanceInfluenceForSnapDuration(f: Float): Float { + var f = f + f -= 0.5f // center the values about 0. + f *= 0.3f * Math.PI / 2.0f + return Math.sin(f) + } + + companion object { + private const val TAG = "DayView" + private const val DEBUG = false + private const val DEBUG_SCALING = false + private const val PERIOD_SPACE = ". " + private var mScale = 0f // Used for supporting different screen densities + private const val INVALID_EVENT_ID: Long = -1 //This is used for remembering a null event + + // Duration of the allday expansion + private const val ANIMATION_DURATION: Long = 400 + + // duration of the more allday event text fade + private const val ANIMATION_SECONDARY_DURATION: Long = 200 + + // duration of the scroll to go to a specified time + private const val GOTO_SCROLL_DURATION = 200 + + // duration for events' cross-fade animation + private const val EVENTS_CROSS_FADE_DURATION = 400 + + // duration to show the event clicked + private const val CLICK_DISPLAY_DURATION = 50 + private const val MENU_DAY = 3 + private const val MENU_EVENT_VIEW = 5 + private const val MENU_EVENT_CREATE = 6 + private const val MENU_EVENT_EDIT = 7 + private const val MENU_EVENT_DELETE = 8 + private var DEFAULT_CELL_HEIGHT = 64 + private var MAX_CELL_HEIGHT = 150 + private var MIN_Y_SPAN = 100 + private val CALENDARS_PROJECTION = arrayOf( + Calendars._ID, // 0 + Calendars.CALENDAR_ACCESS_LEVEL, // 1 + Calendars.OWNER_ACCOUNT + ) + private const val CALENDARS_INDEX_ACCESS_LEVEL = 1 + private const val CALENDARS_INDEX_OWNER_ACCOUNT = 2 + private val CALENDARS_WHERE: String = Calendars._ID.toString() + "=%d" + private const val FROM_NONE = 0 + private const val FROM_ABOVE = 1 + private const val FROM_BELOW = 2 + private const val FROM_LEFT = 4 + private const val FROM_RIGHT = 8 + private const val ACCESS_LEVEL_NONE = 0 + private const val ACCESS_LEVEL_DELETE = 1 + private const val ACCESS_LEVEL_EDIT = 2 + private var mHorizontalSnapBackThreshold = 128 + + //Update the current time line every five minutes if the window is left open that long + private const val UPDATE_CURRENT_TIME_DELAY = 300000 + private var mOnDownDelay: Int + protected var mStringBuilder: StringBuilder = StringBuilder(50) + + // TODO recreate formatter when locale changes + protected var mFormatter: Formatter = Formatter(mStringBuilder, Locale.getDefault()) + + // The number of milliseconds to show the popup window + private const val POPUP_DISMISS_DELAY = 3000 + private var GRID_LINE_LEFT_MARGIN = 0f + private const val GRID_LINE_INNER_WIDTH = 1f + private const val DAY_GAP = 1 + private const val HOUR_GAP = 1 + + // This is the standard height of an allday event with no restrictions + private var SINGLE_ALLDAY_HEIGHT = 34 + + /** + * This is the minimum desired height of a allday event. + * When unexpanded, allday events will use this height. + * When expanded allDay events will attempt to grow to fit all + * events at this height. + */ + private var MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = 28.0f // in pixels + + /** + * This is how big the unexpanded allday height is allowed to be. + * It will get adjusted based on screen size + */ + private var MAX_UNEXPANDED_ALLDAY_HEIGHT = (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4).toInt() + + /** + * This is the minimum size reserved for displaying regular events. + * The expanded allDay region can't expand into this. + */ + private const val MIN_HOURS_HEIGHT = 180 + private var ALLDAY_TOP_MARGIN = 1 + + // The largest a single allDay event will become. + private var MAX_HEIGHT_OF_ONE_ALLDAY_EVENT = 34 + private var HOURS_TOP_MARGIN = 2 + private var HOURS_LEFT_MARGIN = 2 + private var HOURS_RIGHT_MARGIN = 4 + private var HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN + private var NEW_EVENT_MARGIN = 4 + private var NEW_EVENT_WIDTH = 2 + private var NEW_EVENT_MAX_LENGTH = 16 + private var CURRENT_TIME_LINE_SIDE_BUFFER = 4 + private var CURRENT_TIME_LINE_TOP_OFFSET = 2 + + /* package */ + const val MINUTES_PER_HOUR = 60 + + /* package */ + const val MINUTES_PER_DAY = MINUTES_PER_HOUR * 24 + + /* package */ + const val MILLIS_PER_MINUTE = 60 * 1000 + + /* package */ + const val MILLIS_PER_HOUR = 3600 * 1000 + + /* package */ + const val MILLIS_PER_DAY = MILLIS_PER_HOUR * 24 + + // More events text will transition between invisible and this alpha + private const val MORE_EVENTS_MAX_ALPHA = 0x4C + private var DAY_HEADER_ONE_DAY_LEFT_MARGIN = 0 + private var DAY_HEADER_ONE_DAY_RIGHT_MARGIN = 5 + private var DAY_HEADER_ONE_DAY_BOTTOM_MARGIN = 6 + private var DAY_HEADER_RIGHT_MARGIN = 4 + private var DAY_HEADER_BOTTOM_MARGIN = 3 + private var DAY_HEADER_FONT_SIZE = 14f + private var DATE_HEADER_FONT_SIZE = 32f + private var NORMAL_FONT_SIZE = 12f + private var EVENT_TEXT_FONT_SIZE = 12f + private var HOURS_TEXT_SIZE = 12f + private var AMPM_TEXT_SIZE = 9f + private var MIN_HOURS_WIDTH = 96 + private var MIN_CELL_WIDTH_FOR_TEXT = 20 + private const val MAX_EVENT_TEXT_LEN = 500 + + // smallest height to draw an event with + private var MIN_EVENT_HEIGHT = 24.0f // in pixels + private var CALENDAR_COLOR_SQUARE_SIZE = 10 + private var EVENT_RECT_TOP_MARGIN = 1 + private var EVENT_RECT_BOTTOM_MARGIN = 0 + private var EVENT_RECT_LEFT_MARGIN = 1 + private var EVENT_RECT_RIGHT_MARGIN = 0 + private var EVENT_RECT_STROKE_WIDTH = 2 + private var EVENT_TEXT_TOP_MARGIN = 2 + private var EVENT_TEXT_BOTTOM_MARGIN = 2 + private var EVENT_TEXT_LEFT_MARGIN = 6 + private var EVENT_TEXT_RIGHT_MARGIN = 6 + private var ALL_DAY_EVENT_RECT_BOTTOM_MARGIN = 1 + private var EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN + private var EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_BOTTOM_MARGIN + private var EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN + private var EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_RIGHT_MARGIN + + // margins and sizing for the expand allday icon + private var EXPAND_ALL_DAY_BOTTOM_MARGIN = 10 + + // sizing for "box +n" in allDay events + private var EVENT_SQUARE_WIDTH = 10 + private var EVENT_LINE_PADDING = 4 + private var NEW_EVENT_HINT_FONT_SIZE = 12 + private var mEventTextColor = 0 + private var mMoreEventsTextColor = 0 + private var mWeek_saturdayColor = 0 + private var mWeek_sundayColor = 0 + private var mCalendarDateBannerTextColor = 0 + private var mCalendarAmPmLabel = 0 + private var mCalendarGridAreaSelected = 0 + private var mCalendarGridLineInnerHorizontalColor = 0 + private var mCalendarGridLineInnerVerticalColor = 0 + private var mFutureBgColor = 0 + private var mFutureBgColorRes = 0 + private var mBgColor = 0 + private var mNewEventHintColor: Int + private var mCalendarHourLabelColor = 0 + private var mMoreAlldayEventsTextAlpha = MORE_EVENTS_MAX_ALPHA + private var mCellHeight = 0 // shared among all DayViews + private var mMinCellHeight = 32 + private var mScaledPagingTouchSlop = 0 + + /** + * Whether to use the expand or collapse icon. + */ + private var mUseExpandIcon = true + + /** + * The height of the day names/numbers + */ + private var DAY_HEADER_HEIGHT = 45 + + /** + * The height of the day names/numbers for multi-day views + */ + private var MULTI_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT + + /** + * The height of the day names/numbers when viewing a single day + */ + private var ONE_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT + + /** + * Whether or not to expand the allDay area to fill the screen + */ + private var mShowAllAllDayEvents = false + private var sCounter = 0 + + /** + * The initial state of the touch mode when we enter this view. + */ + private const val TOUCH_MODE_INITIAL_STATE = 0 + + /** + * Indicates we just received the touch event and we are waiting to see if + * it is a tap or a scroll gesture. + */ + private const val TOUCH_MODE_DOWN = 1 + + /** + * Indicates the touch gesture is a vertical scroll + */ + private const val TOUCH_MODE_VSCROLL = 0x20 + + /** + * Indicates the touch gesture is a horizontal scroll + */ + private const val TOUCH_MODE_HSCROLL = 0x40 + + /** + * The selection modes are HIDDEN, PRESSED, SELECTED, and LONGPRESS. + */ + private const val SELECTION_HIDDEN = 0 + private const val SELECTION_PRESSED = 1 // D-pad down but not up yet + private const val SELECTION_SELECTED = 2 + private const val SELECTION_LONGPRESS = 3 + + // The rest of this file was borrowed from Launcher2 - PagedView.java + private const val MINIMUM_SNAP_VELOCITY = 2200 + } + + init { + mContext = context + initAccessibilityVariables() + mResources = context.getResources() + mNewEventHintString = mResources.getString(R.string.day_view_new_event_hint) + mNumDays = numDays + DATE_HEADER_FONT_SIZE = + mResources.getDimension(R.dimen.date_header_text_size) as Int.toFloat() + DAY_HEADER_FONT_SIZE = mResources.getDimension(R.dimen.day_label_text_size) as Int.toFloat() + ONE_DAY_HEADER_HEIGHT = mResources.getDimension(R.dimen.one_day_header_height) + DAY_HEADER_BOTTOM_MARGIN = mResources.getDimension(R.dimen.day_header_bottom_margin) + EXPAND_ALL_DAY_BOTTOM_MARGIN = mResources.getDimension(R.dimen.all_day_bottom_margin) + HOURS_TEXT_SIZE = mResources.getDimension(R.dimen.hours_text_size) as Int.toFloat() + AMPM_TEXT_SIZE = mResources.getDimension(R.dimen.ampm_text_size) as Int.toFloat() + MIN_HOURS_WIDTH = mResources.getDimension(R.dimen.min_hours_width) + HOURS_LEFT_MARGIN = mResources.getDimension(R.dimen.hours_left_margin) + HOURS_RIGHT_MARGIN = mResources.getDimension(R.dimen.hours_right_margin) + MULTI_DAY_HEADER_HEIGHT = mResources.getDimension(R.dimen.day_header_height) + val eventTextSizeId: Int + eventTextSizeId = if (mNumDays == 1) { + R.dimen.day_view_event_text_size + } else { + R.dimen.week_view_event_text_size + } + EVENT_TEXT_FONT_SIZE = mResources.getDimension(eventTextSizeId) as Int.toFloat() + NEW_EVENT_HINT_FONT_SIZE = mResources.getDimension(R.dimen.new_event_hint_text_size) + MIN_EVENT_HEIGHT = mResources.getDimension(R.dimen.event_min_height) + MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = MIN_EVENT_HEIGHT + EVENT_TEXT_TOP_MARGIN = mResources.getDimension(R.dimen.event_text_vertical_margin) + EVENT_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN + EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN + EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN + EVENT_TEXT_LEFT_MARGIN = mResources + .getDimension(R.dimen.event_text_horizontal_margin) + EVENT_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN + EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN + if (mScale == 0f) { + mScale = mResources.getDisplayMetrics().density + if (mScale != 1f) { + SINGLE_ALLDAY_HEIGHT *= mScale.toInt() + ALLDAY_TOP_MARGIN *= mScale.toInt() + MAX_HEIGHT_OF_ONE_ALLDAY_EVENT *= mScale.toInt() + NORMAL_FONT_SIZE *= mScale + GRID_LINE_LEFT_MARGIN *= mScale + HOURS_TOP_MARGIN *= mScale.toInt() + MIN_CELL_WIDTH_FOR_TEXT *= mScale.toInt() + MAX_UNEXPANDED_ALLDAY_HEIGHT *= mScale.toInt() + mAnimateDayEventHeight = MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt() + CURRENT_TIME_LINE_SIDE_BUFFER *= mScale.toInt() + CURRENT_TIME_LINE_TOP_OFFSET *= mScale.toInt() + MIN_Y_SPAN *= mScale.toInt() + MAX_CELL_HEIGHT *= mScale.toInt() + DEFAULT_CELL_HEIGHT *= mScale.toInt() + DAY_HEADER_HEIGHT *= mScale.toInt() + DAY_HEADER_RIGHT_MARGIN *= mScale.toInt() + DAY_HEADER_ONE_DAY_LEFT_MARGIN *= mScale.toInt() + DAY_HEADER_ONE_DAY_RIGHT_MARGIN *= mScale.toInt() + DAY_HEADER_ONE_DAY_BOTTOM_MARGIN *= mScale.toInt() + CALENDAR_COLOR_SQUARE_SIZE *= mScale.toInt() + EVENT_RECT_TOP_MARGIN *= mScale.toInt() + EVENT_RECT_BOTTOM_MARGIN *= mScale.toInt() + ALL_DAY_EVENT_RECT_BOTTOM_MARGIN *= mScale.toInt() + EVENT_RECT_LEFT_MARGIN *= mScale.toInt() + EVENT_RECT_RIGHT_MARGIN *= mScale.toInt() + EVENT_RECT_STROKE_WIDTH *= mScale.toInt() + EVENT_SQUARE_WIDTH *= mScale.toInt() + EVENT_LINE_PADDING *= mScale.toInt() + NEW_EVENT_MARGIN *= mScale.toInt() + NEW_EVENT_WIDTH *= mScale.toInt() + NEW_EVENT_MAX_LENGTH *= mScale.toInt() + } + } + HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN + DAY_HEADER_HEIGHT = if (mNumDays == 1) ONE_DAY_HEADER_HEIGHT else MULTI_DAY_HEADER_HEIGHT + mCurrentTimeLine = mResources.getDrawable(R.drawable.timeline_indicator_holo_light) + mCurrentTimeAnimateLine = mResources + .getDrawable(R.drawable.timeline_indicator_activated_holo_light) + mTodayHeaderDrawable = mResources.getDrawable(R.drawable.today_blue_week_holo_light) + mExpandAlldayDrawable = mResources.getDrawable(R.drawable.ic_expand_holo_light) + mCollapseAlldayDrawable = mResources.getDrawable(R.drawable.ic_collapse_holo_light) + mNewEventHintColor = mResources.getColor(R.color.new_event_hint_text_color) + mAcceptedOrTentativeEventBoxDrawable = mResources + .getDrawable(R.drawable.panel_month_event_holo_light) + mEventLoader = eventLoader + mEventGeometry = EventGeometry() + mEventGeometry.setMinEventHeight(MIN_EVENT_HEIGHT) + mEventGeometry.setHourGap(HOUR_GAP) + mEventGeometry.setCellMargin(DAY_GAP) + mLastPopupEventID = INVALID_EVENT_ID + mController = controller + mViewSwitcher = viewSwitcher + mGestureDetector = GestureDetector(context, CalendarGestureListener()) + mScaleGestureDetector = ScaleGestureDetector(getContext(), this) + if (mCellHeight == 0) { + mCellHeight = Utils.getSharedPreference( + mContext, + GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, DEFAULT_CELL_HEIGHT + ) + } + mScroller = OverScroller(context) + mHScrollInterpolator = ScrollInterpolator() + mEdgeEffectTop = EdgeEffect(context) + mEdgeEffectBottom = EdgeEffect(context) + val vc: ViewConfiguration = ViewConfiguration.get(context) + mScaledPagingTouchSlop = vc.getScaledPagingTouchSlop() + mOnDownDelay = ViewConfiguration.getTapTimeout() + OVERFLING_DISTANCE = vc.getScaledOverflingDistance() + init(context) + } +} \ No newline at end of file -- cgit v1.2.3 From 805e4e0e89fea65ba11abbeedced440ef1333ad7 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 2 Jul 2021 18:24:51 +0000 Subject: AOSP/Calendar - DayView fully converted with bp file This is the fully converted and touched-up Kotlin file after the converted was run once. Only one import statement was translated over to the converted file. The rest were deleted. Moreover, the "override" modifier had to be manually added various times to avoid compilation errors. There were also many type mismatches that had to corrected to allow Calendar to successfully build. Regarding runtime errors, a null pointer exception that was causing the Calendar app to crash was corrected by making a certain variables nullable. The converter also seemed to automatically cast certain numberical types with the "as" keyword. For example, "as Long" and "as Int" were the source of other runtime exceptions that caused the app to crash. Invoking ".toInt()" and "toLong()" corrected this issue. A few minor tweaks had to made to DayFragment in order to accommodate for the changes made in DayView. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.157 General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 6m 14s Total aggregated tests run time: 6m 14s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 1.01 tests/sec [376 tests / 374028 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 12179 ms || clean = 2877 ms Total preparation time: 12s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 8m 6s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: Ieaf65157b9b748b7a9f540edc0922bb6fabae949 --- Android.bp | 1 + src/com/android/calendar/DayFragment.kt | 6 +- src/com/android/calendar/DayView.kt | 974 +++++++++++++++++--------------- 3 files changed, 538 insertions(+), 443 deletions(-) diff --git a/Android.bp b/Android.bp index af827ed4..fd318cc3 100644 --- a/Android.bp +++ b/Android.bp @@ -34,6 +34,7 @@ exclude_srcsd = [ "src/**/calendar/AllInOneActivity.java", "src/**/calendar/CalendarController.java", "src/**/calendar/DayOfMonthDrawable.java", + "src/**/calendar/DayView.java", "src/**/calendar/Event.java", "src/**/calendar/EventInfoActivity.java", "src/**/calendar/StickyHeaderListView.java", diff --git a/src/com/android/calendar/DayFragment.kt b/src/com/android/calendar/DayFragment.kt index 63800951..39e92f5b 100644 --- a/src/com/android/calendar/DayFragment.kt +++ b/src/com/android/calendar/DayFragment.kt @@ -151,7 +151,7 @@ class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { val currentView: DayView? = mViewSwitcher?.getCurrentView() as? DayView // How does goTo time compared to what's already displaying? - val diff: Int = currentView?.compareToVisibleTimeRange(goToTime) as Int + val diff: Int = currentView?.compareToVisibleTimeRange(goToTime as Time) as Int if (diff == 0) { // In visible range. No need to switch view currentView?.setSelected(goToTime, ignoreTime, animateToday) @@ -166,7 +166,7 @@ class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { } val next: DayView? = mViewSwitcher?.getNextView() as? DayView if (ignoreTime) { - next?.setFirstVisibleHour(currentView?.getFirstVisibleHour()) + next!!.firstVisibleHour = currentView.firstVisibleHour } next?.setSelected(goToTime, ignoreTime, animateToday) next?.reloadEvents() @@ -190,7 +190,7 @@ class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { return -1 } val view: DayView = mViewSwitcher?.getCurrentView() as DayView ?: return -1 - return view.getSelectedTimeInMillis() + return view.selectedTimeInMillis } override fun eventsChanged() { diff --git a/src/com/android/calendar/DayView.kt b/src/com/android/calendar/DayView.kt index ce8ad124..58126f20 100644 --- a/src/com/android/calendar/DayView.kt +++ b/src/com/android/calendar/DayView.kt @@ -16,13 +16,74 @@ package com.android.calendar import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ObjectAnimator +import android.animation.ValueAnimator +import android.app.Service +import android.content.Context +import android.content.res.Resources +import android.content.res.TypedArray +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Paint.Align +import android.graphics.Paint.Style +import android.graphics.Rect +import android.graphics.Typeface +import android.graphics.drawable.Drawable +import android.os.Handler +import android.provider.CalendarContract.Attendees +import android.provider.CalendarContract.Calendars +import android.text.Layout.Alignment +import android.text.SpannableStringBuilder +import android.text.StaticLayout +import android.text.TextPaint +import android.text.format.DateFormat +import android.text.format.DateUtils +import android.text.format.Time +import android.text.style.StyleSpan +import android.util.Log +import android.view.ContextMenu +import android.view.ContextMenu.ContextMenuInfo +import android.view.GestureDetector +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.MotionEvent +import android.view.ScaleGestureDetector +import android.view.View +import android.view.ViewConfiguration +import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams +import android.view.WindowManager +import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityManager +import android.view.animation.AccelerateDecelerateInterpolator +import android.view.animation.Animation +import android.view.animation.Interpolator +import android.view.animation.TranslateAnimation +import android.widget.EdgeEffect +import android.widget.OverScroller +import android.widget.PopupWindow +import android.widget.ViewSwitcher +import com.android.calendar.CalendarController.EventType +import com.android.calendar.CalendarController.ViewType +import java.util.ArrayList +import java.util.Arrays +import java.util.Calendar +import java.util.Formatter +import java.util.Locale +import java.util.regex.Matcher +import java.util.regex.Pattern /** * View for multi-day view. So far only 1 and 7 day have been tested. */ class DayView( - context: Context, controller: CalendarController, - viewSwitcher: ViewSwitcher, eventLoader: EventLoader, numDays: Int + context: Context?, + controller: CalendarController?, + viewSwitcher: ViewSwitcher?, + eventLoader: EventLoader?, + numDays: Int ) : View(context), View.OnCreateContextMenuListener, ScaleGestureDetector.OnScaleGestureListener, View.OnClickListener, View.OnLongClickListener { private var mOnFlingCalled = false @@ -39,7 +100,7 @@ class DayView( * explicitly scrolls to an empty time slot, changes views, or deletes the event. */ private var mLastPopupEventID: Long - protected var mContext: Context + protected var mContext: Context? = null private val mContinueScroll: ContinueScroll = ContinueScroll() // Make this visible within the package for more informative debugging @@ -54,33 +115,30 @@ class DayView( private var mMonthLength = 0 private var mFirstVisibleDate = 0 private var mFirstVisibleDayOfWeek = 0 - private var mEarliestStartHour // indexed by the week day offset - : IntArray - private var mHasAllDayEvent // indexed by the week day offset - : BooleanArray + private var mEarliestStartHour: IntArray? = null // indexed by the week day offset + private var mHasAllDayEvent: BooleanArray? = null // indexed by the week day offset private var mEventCountTemplate: String? = null - private var mClickedEvent // The event the user clicked on - : Event? = null + private var mClickedEvent: Event? = null // The event the user clicked on private var mSavedClickedEvent: Event? = null private var mClickedYLocation = 0 private var mDownTouchTime: Long = 0 private var mEventsAlpha = 255 private var mEventsCrossFadeAnimation: ObjectAnimator? = null - private val mTZUpdater: Runnable = object : Runnable() { + private val mTZUpdater: Runnable = object : Runnable { @Override - fun run() { - val tz: String = Utils.getTimeZone(mContext, this) - mBaseDate.timezone = tz - mBaseDate.normalize(true) - mCurrentTime.switchTimezone(tz) + override fun run() { + val tz: String? = Utils.getTimeZone(mContext, this) + mBaseDate!!.timezone = tz + mBaseDate?.normalize(true) + mCurrentTime?.switchTimezone(tz) invalidate() } } // Sets the "clicked" color from the clicked event - private val mSetClick: Runnable = object : Runnable() { + private val mSetClick: Runnable = object : Runnable { @Override - fun run() { + override fun run() { mClickedEvent = mSavedClickedEvent mSavedClickedEvent = null this@DayView.invalidate() @@ -88,13 +146,13 @@ class DayView( } // Clears the "clicked" color from the clicked event and launch the event - private val mClearClick: Runnable = object : Runnable() { + private val mClearClick: Runnable = object : Runnable { @Override - fun run() { + override fun run() { if (mClickedEvent != null) { - mController.sendEventRelatedEvent( - this, EventType.VIEW_EVENT, mClickedEvent.id, - mClickedEvent.startMillis, mClickedEvent.endMillis, + mController?.sendEventRelatedEvent( + this as Object?, EventType.VIEW_EVENT, mClickedEvent!!.id, + mClickedEvent!!.startMillis, mClickedEvent!!.endMillis, this@DayView.getWidth() / 2, mClickedYLocation, selectedTimeInMillis ) @@ -112,7 +170,7 @@ class DayView( @Volatile private var mFadingIn = false @Override - fun onAnimationEnd(animation: Animator) { + override fun onAnimationEnd(animation: Animator) { synchronized(this) { if (mAnimator !== animation) { animation.removeAllListeners() @@ -121,20 +179,20 @@ class DayView( } if (mFadingIn) { if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners() - mTodayAnimator.cancel() + mTodayAnimator?.removeAllListeners() + mTodayAnimator?.cancel() } mTodayAnimator = ObjectAnimator .ofInt(this@DayView, "animateTodayAlpha", 255, 0) mAnimator = mTodayAnimator mFadingIn = false - mTodayAnimator.addListener(this) - mTodayAnimator.setDuration(600) - mTodayAnimator.start() + mTodayAnimator?.addListener(this) + mTodayAnimator?.setDuration(600) + mTodayAnimator?.start() } else { mAnimateToday = false mAnimateTodayAlpha = 0 - mAnimator.removeAllListeners() + mAnimator?.removeAllListeners() mAnimator = null mTodayAnimator = null invalidate() @@ -153,17 +211,17 @@ class DayView( var mAnimatorListener: AnimatorListenerAdapter = object : AnimatorListenerAdapter() { @Override - fun onAnimationStart(animation: Animator?) { + override fun onAnimationStart(animation: Animator?) { mScrolling = true } @Override - fun onAnimationCancel(animation: Animator?) { + override fun onAnimationCancel(animation: Animator?) { mScrolling = false } @Override - fun onAnimationEnd(animation: Animator?) { + override fun onAnimationEnd(animation: Animator?) { mScrolling = false resetSelectedHour() invalidate() @@ -182,14 +240,12 @@ class DayView( private var mAllDayEvents: ArrayList? = ArrayList() private var mLayouts: Array? = null private var mAllDayLayouts: Array? = null - private var mSelectionDay // Julian day - = 0 + private var mSelectionDay = 0 // Julian day private var mSelectionHour = 0 var mSelectionAllday = false // Current selection info for accessibility - private var mSelectionDayForAccessibility // Julian day - = 0 + private var mSelectionDayForAccessibility = 0 // Julian day private var mSelectionHourForAccessibility = 0 private var mSelectedEventForAccessibility: Event? = null @@ -213,9 +269,8 @@ class DayView( private val mPaint: Paint = Paint() private val mEventTextPaint: Paint = Paint() private val mSelectionPaint: Paint = Paint() - private var mLines: FloatArray - private var mFirstDayOfWeek // First day of the week - = 0 + private var mLines: FloatArray = emptyArray().toFloatArray() + private var mFirstDayOfWeek = 0 // First day of the week private var mPopup: PopupWindow? = null private var mPopupView: View? = null private val mDismissPopup: DismissPopup = DismissPopup() @@ -276,7 +331,7 @@ class DayView( /** * A count of the number of allday events that were not drawn for each day */ - private var mSkippedAlldayEvents: IntArray? + private var mSkippedAlldayEvents: IntArray? = null /** * The number of allDay events at which point we start hiding allDay events. @@ -297,9 +352,9 @@ class DayView( /** Distance between the mFirstCell and the top of first fully visible hour. */ private var mFirstHourOffset = 0 - private var mHourStrs: Array - private var mDayStrs: Array - private var mDayStrs2Letter: Array + private var mHourStrs: Array? = null + private var mDayStrs: Array? = null + private var mDayStrs2Letter: Array? = null private var mIs24HourFormat = false private val mSelectedEvents: ArrayList = ArrayList() private var mComputeSelectedEvents = false @@ -359,10 +414,10 @@ class DayView( private var mTouchExplorationEnabled = false private val mNewEventHintString: String @Override - protected fun onAttachedToWindow() { + protected override fun onAttachedToWindow() { if (mHandler == null) { mHandler = getHandler() - mHandler.post(mUpdateCurrentTime) + mHandler?.post(mUpdateCurrentTime) } } @@ -377,8 +432,8 @@ class DayView( mFirstDayOfWeek = Utils.getFirstDayOfWeek(context) mCurrentTime = Time(Utils.getTimeZone(context, mTZUpdater)) val currentTime: Long = System.currentTimeMillis() - mCurrentTime.set(currentTime) - mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff) + mCurrentTime?.set(currentTime) + mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime!!.gmtoff) mWeek_saturdayColor = mResources.getColor(R.color.week_saturday) mWeek_sundayColor = mResources.getColor(R.color.week_sunday) mCalendarDateBannerTextColor = mResources.getColor(R.color.calendar_date_banner_text_color) @@ -413,18 +468,19 @@ class DayView( for (i in Calendar.SUNDAY..Calendar.SATURDAY) { val index: Int = i - Calendar.SUNDAY // e.g. Tue for Tuesday - mDayStrs[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_MEDIUM) + mDayStrs!![index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_MEDIUM) .toUpperCase() - mDayStrs[index + 7] = mDayStrs[index] + mDayStrs!![index + 7] = mDayStrs!![index] // e.g. Tu for Tuesday - mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORT) + mDayStrs2Letter!![index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORT) .toUpperCase() // If we don't have 2-letter day strings, fall back to 1-letter. - if (mDayStrs2Letter[index]!!.equals(mDayStrs[index])) { - mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORTEST) + if (mDayStrs2Letter!![index]!!.equals(mDayStrs!![index])) { + mDayStrs2Letter!![index] = DateUtils.getDayOfWeekString(i, + DateUtils.LENGTH_SHORTEST) } - mDayStrs2Letter[index + 7] = mDayStrs2Letter[index] + mDayStrs2Letter!![index + 7] = mDayStrs2Letter!![index] } // Figure out how much space we need for the 3-letter abbrev names @@ -434,7 +490,7 @@ class DayView( val dateStrs = arrayOf(" 28", " 30") mDateStrWidth = computeMaxStringWidth(0, dateStrs, p) p.setTextSize(DAY_HEADER_FONT_SIZE) - mDateStrWidth += computeMaxStringWidth(0, mDayStrs, p) + mDateStrWidth += computeMaxStringWidth(0, mDayStrs as Array, p) p.setTextSize(HOURS_TEXT_SIZE) p.setTypeface(null) handleOnResume() @@ -443,21 +499,21 @@ class DayView( val ampm = arrayOf(mAmString, mPmString) p.setTextSize(AMPM_TEXT_SIZE) mHoursWidth = Math.max( - HOURS_MARGIN, computeMaxStringWidth(mHoursWidth, ampm, p) - + HOURS_RIGHT_MARGIN + HOURS_MARGIN, computeMaxStringWidth(mHoursWidth, ampm, p) + + HOURS_RIGHT_MARGIN ) mHoursWidth = Math.max(MIN_HOURS_WIDTH, mHoursWidth) val inflater: LayoutInflater inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater mPopupView = inflater.inflate(R.layout.bubble_event, null) - mPopupView.setLayoutParams( + mPopupView?.setLayoutParams( LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ) ) mPopup = PopupWindow(context) - mPopup.setContentView(mPopupView) + mPopup?.setContentView(mPopupView) val dialogTheme: Resources.Theme = getResources().newTheme() dialogTheme.applyStyle(android.R.style.Theme_Dialog, true) val ta: TypedArray = dialogTheme.obtainStyledAttributes( @@ -465,16 +521,16 @@ class DayView( android.R.attr.windowBackground ) ) - mPopup.setBackgroundDrawable(ta.getDrawable(0)) + mPopup?.setBackgroundDrawable(ta.getDrawable(0)) ta.recycle() // Enable touching the popup window - mPopupView.setOnClickListener(this) + mPopupView?.setOnClickListener(this) // Catch long clicks for creating a new event setOnLongClickListener(this) mBaseDate = Time(Utils.getTimeZone(context, mTZUpdater)) val millis: Long = System.currentTimeMillis() - mBaseDate.set(millis) + mBaseDate?.set(millis) mEarliestStartHour = IntArray(mNumDays) mHasAllDayEvent = BooleanArray(mNumDays) @@ -482,15 +538,15 @@ class DayView( // drawGridBackground() and drawAllDayEvents(). Its size depends // on the max number of lines that can ever be drawn by any single // drawLines() call in either of those methods. - val maxGridLines = (24 + 1 // max horizontal lines we might draw - + (mNumDays + 1)) // max vertical lines we might draw + val maxGridLines = (24 + 1 + // max horizontal lines we might draw + (mNumDays + 1)) // max vertical lines we might draw mLines = FloatArray(maxGridLines * 4) } /** * This is called when the popup window is pressed. */ - fun onClick(v: View) { + override fun onClick(v: View) { if (v === mPopupView) { // Pretend it was a trackball click because that will always // jump to the "View event" screen. @@ -516,10 +572,10 @@ class DayView( private fun initAccessibilityVariables() { mAccessibilityMgr = mContext - .getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager - mIsAccessibilityEnabled = mAccessibilityMgr != null && mAccessibilityMgr.isEnabled() + ?.getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager + mIsAccessibilityEnabled = mAccessibilityMgr != null && mAccessibilityMgr!!.isEnabled() mTouchExplorationEnabled = isTouchExplorationEnabled - }/* ignore isDst */// We ignore the "isDst" field because we want normalize() to figure + } /* ignore isDst */ // We ignore the "isDst" field because we want normalize() to figure // out the correct DST value and not adjust the selected time based // on the current setting of DST. /** @@ -537,7 +593,7 @@ class DayView( // out the correct DST value and not adjust the selected time based // on the current setting of DST. return time.normalize(true /* ignore isDst */) - }/* ignore isDst */ + } /* ignore isDst */ // We ignore the "isDst" field because we want normalize() to figure // out the correct DST value and not adjust the selected time based @@ -553,7 +609,7 @@ class DayView( // on the current setting of DST. time.normalize(true /* ignore isDst */) return time - }/* ignore isDst */ + } /* ignore isDst */ // We ignore the "isDst" field because we want normalize() to figure // out the correct DST value and not adjust the selected time based @@ -586,38 +642,38 @@ class DayView( } fun setSelected(time: Time?, ignoreTime: Boolean, animateToday: Boolean) { - mBaseDate.set(time) - setSelectedHour(mBaseDate.hour) + mBaseDate?.set(time) + setSelectedHour(mBaseDate!!.hour) setSelectedEvent(null) mPrevSelectedEvent = null - val millis: Long = mBaseDate.toMillis(false /* use isDst */) - selectedDay = Time.getJulianDay(millis, mBaseDate.gmtoff) + val millis: Long = mBaseDate!!.toMillis(false /* use isDst */) + setSelectedDay(Time.getJulianDay(millis, mBaseDate!!.gmtoff)) mSelectedEvents.clear() mComputeSelectedEvents = true var gotoY: Int = Integer.MIN_VALUE if (!ignoreTime && mGridAreaHeight != -1) { var lastHour = 0 - if (mBaseDate.hour < mFirstHour) { + if (mBaseDate!!.hour < mFirstHour) { // Above visible region - gotoY = mBaseDate.hour * (mCellHeight + HOUR_GAP) + gotoY = mBaseDate!!.hour * (mCellHeight + HOUR_GAP) } else { - lastHour = ((mGridAreaHeight - mFirstHourOffset) / (mCellHeight + HOUR_GAP) - + mFirstHour) - if (mBaseDate.hour >= lastHour) { + lastHour = ((mGridAreaHeight - mFirstHourOffset) / (mCellHeight + HOUR_GAP) + + mFirstHour) + if (mBaseDate!!.hour >= lastHour) { // Below visible region // target hour + 1 (to give it room to see the event) - // grid height (to get the y of the top of the visible // region) - gotoY = ((mBaseDate.hour + 1 + mBaseDate.minute / 60.0f) - * (mCellHeight + HOUR_GAP) - mGridAreaHeight) + gotoY = ((mBaseDate!!.hour + 1 + mBaseDate!!.minute / 60.0f) * + (mCellHeight + HOUR_GAP) - mGridAreaHeight).toInt() } } if (DEBUG) { Log.e( - TAG, "Go " + gotoY + " 1st " + mFirstHour + ":" + mFirstHourOffset + "CH " - + (mCellHeight + HOUR_GAP) + " lh " + lastHour + " gh " + mGridAreaHeight - + " ymax " + mMaxViewStartY + TAG, "Go " + gotoY + " 1st " + mFirstHour + ":" + mFirstHourOffset + "CH " + + (mCellHeight + HOUR_GAP) + " lh " + lastHour + " gh " + mGridAreaHeight + + " ymax " + mMaxViewStartY ) } if (gotoY > mMaxViewStartY) { @@ -633,7 +689,7 @@ class DayView( if (gotoY != Integer.MIN_VALUE) { val scrollAnim: ValueAnimator = ObjectAnimator.ofInt(this, "viewStartY", mViewStartY, gotoY) - scrollAnim.setDuration(GOTO_SCROLL_DURATION) + scrollAnim.setDuration(GOTO_SCROLL_DURATION.toLong()) scrollAnim.setInterpolator(AccelerateDecelerateInterpolator()) scrollAnim.addListener(mAnimatorListener) scrollAnim.start() @@ -642,8 +698,8 @@ class DayView( if (animateToday) { synchronized(mTodayAnimatorListener) { if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners() - mTodayAnimator.cancel() + mTodayAnimator?.removeAllListeners() + mTodayAnimator?.cancel() } mTodayAnimator = ObjectAnimator.ofInt( this, "animateTodayAlpha", @@ -652,12 +708,12 @@ class DayView( mAnimateToday = true mTodayAnimatorListener.setFadingIn(true) mTodayAnimatorListener.setAnimator(mTodayAnimator) - mTodayAnimator.addListener(mTodayAnimatorListener) - mTodayAnimator.setDuration(150) + mTodayAnimator?.addListener(mTodayAnimatorListener) + mTodayAnimator?.setDuration(150) if (delayAnimateToday) { - mTodayAnimator.setStartDelay(GOTO_SCROLL_DURATION) + mTodayAnimator?.setStartDelay(GOTO_SCROLL_DURATION.toLong()) } - mTodayAnimator.start() + mTodayAnimator?.start() } } sendAccessibilityEventAsNeeded(false) @@ -677,27 +733,19 @@ class DayView( fun setAnimateTodayAlpha(todayAlpha: Int) { mAnimateTodayAlpha = todayAlpha invalidate() - }/* ignore isDst */ + } /* ignore isDst */ - // We ignore the "isDst" field because we want normalize() to figure - // out the correct DST value and not adjust the selected time based - // on the current setting of DST. - var selectedDay: Time - get() { - val time = Time(mBaseDate) - time.setJulianDay(mSelectionDay) - time.hour = mSelectionHour + fun getSelectedDay(): Time { + val time = Time(mBaseDate) + time.setJulianDay(mSelectionDay) + time.hour = mSelectionHour - // We ignore the "isDst" field because we want normalize() to figure - // out the correct DST value and not adjust the selected time based - // on the current setting of DST. - time.normalize(true /* ignore isDst */) - return time - } - private set(d) { - mSelectionDay = d - mSelectionDayForAccessibility = d - } + // We ignore the "isDst" field because we want normalize() to figure + // out the correct DST value and not adjust the selected time based + // on the current setting of DST. + time.normalize(true /* ignore isDst */) + return time + } fun updateTitle() { val start = Time(mBaseDate) @@ -707,18 +755,19 @@ class DayView( // Move it forward one minute so the formatter doesn't lose a day end.minute += 1 end.normalize(true) - var formatFlags: Long = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_YEAR + var formatFlags: Long = DateUtils.FORMAT_SHOW_DATE.toLong() or + DateUtils.FORMAT_SHOW_YEAR.toLong() if (mNumDays != 1) { // Don't show day of the month if for multi-day view - formatFlags = formatFlags or DateUtils.FORMAT_NO_MONTH_DAY + formatFlags = formatFlags or DateUtils.FORMAT_NO_MONTH_DAY.toLong() // Abbreviate the month if showing multiple months if (start.month !== end.month) { - formatFlags = formatFlags or DateUtils.FORMAT_ABBREV_MONTH + formatFlags = formatFlags or DateUtils.FORMAT_ABBREV_MONTH.toLong() } } mController.sendEvent( - this, EventType.UPDATE_TITLE, start, end, null, -1, ViewType.CURRENT, + this as Object?, EventType.UPDATE_TITLE, start, end, null, -1, ViewType.CURRENT, formatFlags, null, null ) } @@ -729,12 +778,12 @@ class DayView( * if it is in the visible time range. */ fun compareToVisibleTimeRange(time: Time): Int { - val savedHour: Int = mBaseDate.hour - val savedMinute: Int = mBaseDate.minute - val savedSec: Int = mBaseDate.second - mBaseDate.hour = 0 - mBaseDate.minute = 0 - mBaseDate.second = 0 + val savedHour: Int = mBaseDate!!.hour + val savedMinute: Int = mBaseDate!!.minute + val savedSec: Int = mBaseDate!!.second + mBaseDate!!.hour = 0 + mBaseDate!!.minute = 0 + mBaseDate!!.second = 0 if (DEBUG) { Log.d(TAG, "Begin " + mBaseDate.toString()) Log.d(TAG, "Diff " + time.toString()) @@ -744,12 +793,12 @@ class DayView( var diff: Int = Time.compare(time, mBaseDate) if (diff > 0) { // Compare end of range - mBaseDate.monthDay += mNumDays - mBaseDate.normalize(true) + mBaseDate!!.monthDay += mNumDays + mBaseDate?.normalize(true) diff = Time.compare(time, mBaseDate) if (DEBUG) Log.d(TAG, "End " + mBaseDate.toString()) - mBaseDate.monthDay -= mNumDays - mBaseDate.normalize(true) + mBaseDate!!.monthDay -= mNumDays + mBaseDate?.normalize(true) if (diff < 0) { // in visible time diff = 0 @@ -759,9 +808,9 @@ class DayView( } } if (DEBUG) Log.d(TAG, "Diff: $diff") - mBaseDate.hour = savedHour - mBaseDate.minute = savedMinute - mBaseDate.second = savedSec + mBaseDate!!.hour = savedHour + mBaseDate!!.minute = savedMinute + mBaseDate!!.second = savedSec return diff } @@ -771,28 +820,28 @@ class DayView( if (mNumDays == 7) { adjustToBeginningOfWeek(mBaseDate) } - val start: Long = mBaseDate.toMillis(false /* use isDst */) - mFirstJulianDay = Time.getJulianDay(start, mBaseDate.gmtoff) + val start: Long = mBaseDate!!.toMillis(false /* use isDst */) + mFirstJulianDay = Time.getJulianDay(start, mBaseDate!!.gmtoff) mLastJulianDay = mFirstJulianDay + mNumDays - 1 - mMonthLength = mBaseDate.getActualMaximum(Time.MONTH_DAY) - mFirstVisibleDate = mBaseDate.monthDay - mFirstVisibleDayOfWeek = mBaseDate.weekDay + mMonthLength = mBaseDate!!.getActualMaximum(Time.MONTH_DAY) + mFirstVisibleDate = mBaseDate!!.monthDay + mFirstVisibleDayOfWeek = mBaseDate!!.weekDay } private fun adjustToBeginningOfWeek(time: Time?) { - val dayOfWeek: Int = time.weekDay + val dayOfWeek: Int = time!!.weekDay var diff = dayOfWeek - mFirstDayOfWeek if (diff != 0) { if (diff < 0) { diff += 7 } - time.monthDay -= diff - time.normalize(true /* ignore isDst */) + time!!.monthDay -= diff + time?.normalize(true /* ignore isDst */) } } @Override - protected fun onSizeChanged(width: Int, height: Int, oldw: Int, oldh: Int) { + protected override fun onSizeChanged(width: Int, height: Int, oldw: Int, oldh: Int) { mViewWidth = width mViewHeight = height mEdgeEffectTop.setSize(mViewWidth, mViewHeight) @@ -804,7 +853,7 @@ class DayView( mHorizontalSnapBackThreshold = width / 7 val p = Paint() p.setTextSize(HOURS_TEXT_SIZE) - mHoursTextHeight = Math.abs(p.ascent()) + mHoursTextHeight = Math.abs(p.ascent()).toInt() remeasure(width, height) } @@ -826,8 +875,8 @@ class DayView( // First, clear the array of earliest start times, and the array // indicating presence of an all-day event. for (day in 0 until mNumDays) { - mEarliestStartHour[day] = 25 // some big number - mHasAllDayEvent[day] = false + mEarliestStartHour!![day] = 25 // some big number + mHasAllDayEvent!![day] = false } val maxAllDayEvents = mMaxAlldayEvents @@ -867,7 +916,7 @@ class DayView( // But clip the area depending on which mode we're in if (!mShowAllAllDayEvents && allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) { allDayHeight = (mMaxUnexpandedAlldayEventCount * - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt() + MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt() } else if (allDayHeight > maxAllAllDayHeight) { allDayHeight = maxAllAllDayHeight } @@ -887,16 +936,16 @@ class DayView( EVENT_ALL_DAY_TEXT_LEFT_MARGIN ) mExpandAllDayRect.right = Math.min( - mExpandAllDayRect.left + allDayIconWidth, mHoursWidth - - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN + mExpandAllDayRect.left + allDayIconWidth, mHoursWidth - + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN ) mExpandAllDayRect.bottom = mFirstCell - EXPAND_ALL_DAY_BOTTOM_MARGIN - mExpandAllDayRect.top = (mExpandAllDayRect.bottom - - mExpandAlldayDrawable.getIntrinsicHeight()) + mExpandAllDayRect.top = (mExpandAllDayRect.bottom - + mExpandAlldayDrawable.getIntrinsicHeight()) mNumHours = mGridAreaHeight / (mCellHeight + HOUR_GAP) - mEventGeometry.setHourHeight(mCellHeight) + mEventGeometry.setHourHeight(mCellHeight.toFloat()) val minimumDurationMillis = - (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f)) as Long + (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f)).toLong() Event.computePositions(mEvents, minimumDurationMillis) // Compute the top of our reachable view @@ -923,12 +972,12 @@ class DayView( } mViewStartY = mFirstHour * (mCellHeight + HOUR_GAP) - mFirstHourOffset val eventAreaWidth = mNumDays * (mCellWidth + DAY_GAP) - //When we get new events we don't want to dismiss the popup unless the event changes - if (mSelectedEvent != null && mLastPopupEventID != mSelectedEvent.id) { - mPopup.dismiss() + // When we get new events we don't want to dismiss the popup unless the event changes + if (mSelectedEvent != null && mLastPopupEventID != mSelectedEvent!!.id) { + mPopup?.dismiss() } - mPopup.setWidth(eventAreaWidth - 20) - mPopup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT) + mPopup?.setWidth(eventAreaWidth - 20) + mPopup?.setHeight(WindowManager.LayoutParams.WRAP_CONTENT) } /** @@ -950,7 +999,7 @@ class DayView( view.setSelectedEvent(null) view.mPrevSelectedEvent = null view.mFirstDayOfWeek = mFirstDayOfWeek - if (view.mEvents.size() > 0) { + if (view.mEvents.size > 0) { view.mSelectionAllday = mSelectionAllday } else { view.mSelectionAllday = false @@ -971,7 +1020,7 @@ class DayView( */ private fun switchViews(trackBallSelection: Boolean) { val selectedEvent: Event? = mSelectedEvent - mPopup.dismiss() + mPopup?.dismiss() mLastPopupEventID = INVALID_EVENT_ID if (mNumDays > 1) { // This is the Week view. @@ -981,7 +1030,7 @@ class DayView( if (trackBallSelection) { if (selectedEvent != null) { if (mIsAccessibilityEnabled) { - mAccessibilityMgr.interrupt() + mAccessibilityMgr?.interrupt() } } } @@ -989,23 +1038,23 @@ class DayView( } @Override - fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { + override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { mScrolling = false return super.onKeyUp(keyCode, event) } @Override - fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { return super.onKeyDown(keyCode, event) } @Override - fun onHoverEvent(event: MotionEvent?): Boolean { + override fun onHoverEvent(event: MotionEvent?): Boolean { return true } private val isTouchExplorationEnabled: Boolean - private get() = mIsAccessibilityEnabled && mAccessibilityMgr.isTouchExplorationEnabled() + private get() = mIsAccessibilityEnabled && mAccessibilityMgr!!.isTouchExplorationEnabled() private fun sendAccessibilityEventAsNeeded(speakEvents: Boolean) { if (!mIsAccessibilityEnabled) { @@ -1013,7 +1062,8 @@ class DayView( } val dayChanged = mLastSelectionDayForAccessibility != mSelectionDayForAccessibility val hourChanged = mLastSelectionHourForAccessibility != mSelectionHourForAccessibility - if (dayChanged || hourChanged || mLastSelectedEventForAccessibility !== mSelectedEventForAccessibility) { + if (dayChanged || hourChanged || mLastSelectedEventForAccessibility !== + mSelectedEventForAccessibility) { mLastSelectionDayForAccessibility = mSelectionDayForAccessibility mLastSelectionHourForAccessibility = mSelectionHourForAccessibility mLastSelectedEventForAccessibility = mSelectedEventForAccessibility @@ -1031,11 +1081,11 @@ class DayView( } if (speakEvents) { if (mEventCountTemplate == null) { - mEventCountTemplate = mContext.getString(R.string.template_announce_item_index) + mEventCountTemplate = mContext?.getString(R.string.template_announce_item_index) } // Read out the relevant event(s) - val numEvents: Int = mSelectedEvents.size() + val numEvents: Int = mSelectedEvents.size if (numEvents > 0) { if (mSelectedEventForAccessibility == null) { // Read out all the events @@ -1070,7 +1120,7 @@ class DayView( .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED) val msg: CharSequence = b.toString() event.getText().add(msg) - event.setAddedCount(msg.length()) + event.setAddedCount(msg.length) sendAccessibilityEventUnchecked(event) } } @@ -1081,11 +1131,11 @@ class DayView( * @param calEvent */ private fun appendEventAccessibilityString(b: StringBuilder, calEvent: Event?) { - b.append(calEvent.getTitleAndLocation()) + b.append(calEvent!!.titleAndLocation) b.append(PERIOD_SPACE) - val `when`: String + val `when`: String? var flags: Int = DateUtils.FORMAT_SHOW_DATE - if (calEvent.allDay) { + if (calEvent!!.allDay) { flags = flags or (DateUtils.FORMAT_UTC or DateUtils.FORMAT_SHOW_WEEKDAY) } else { flags = flags or DateUtils.FORMAT_SHOW_TIME @@ -1093,7 +1143,8 @@ class DayView( flags = flags or DateUtils.FORMAT_24HOUR } } - `when` = Utils.formatDateRange(mContext, calEvent.startMillis, calEvent.endMillis, flags) + `when` = Utils.formatDateRange(mContext, calEvent!!.startMillis, calEvent!!.endMillis, + flags) b.append(`when`) b.append(PERIOD_SPACE) } @@ -1103,25 +1154,25 @@ class DayView( private val mStart: Time private val mEnd: Time @Override - fun onAnimationEnd(animation: Animation?) { + override fun onAnimationEnd(animation: Animation?) { var view = mViewSwitcher.getCurrentView() as DayView view.mViewStartX = 0 - view = mViewSwitcher.getNextView() + view = mViewSwitcher.getNextView() as DayView view.mViewStartX = 0 if (mCounter == sCounter) { mController.sendEvent( - this, EventType.GO_TO, mStart, mEnd, null, -1, + this as Object?, EventType.GO_TO, mStart, mEnd, null, -1, ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null ) } } @Override - fun onAnimationRepeat(animation: Animation?) { + override fun onAnimationRepeat(animation: Animation?) { } @Override - fun onAnimationStart(animation: Animation?) { + override fun onAnimationStart(animation: Animation?) { } init { @@ -1155,14 +1206,14 @@ class DayView( outFromXValue = progress outToXValue = 1.0f } - val start = Time(mBaseDate.timezone) - start.set(mController.getTime()) + val start = Time(mBaseDate!!.timezone) + start.set(mController.time as Long) if (forward) { start.monthDay += mNumDays } else { start.monthDay -= mNumDays } - mController.setTime(start.normalize(true)) + mController.time = start.normalize(true) var newSelected: Time? = start if (mNumDays == 7) { newSelected = Time(start) @@ -1196,7 +1247,7 @@ class DayView( var view = mViewSwitcher.getCurrentView() as DayView view.cleanup() mViewSwitcher.showNext() - view = mViewSwitcher.getCurrentView() + view = mViewSwitcher.getCurrentView() as DayView view.setSelected(newSelected, true, false) view.requestFocus() view.reloadEvents() @@ -1263,7 +1314,9 @@ class DayView( // Exception 2: if 12am is on screen, then allow the user to select // 12am before going up to the all-day event area. val daynum = mSelectionDay - mFirstJulianDay - if (daynum < mEarliestStartHour.size && daynum >= 0 && mMaxAlldayEvents > 0 && mEarliestStartHour[daynum] > mSelectionHour && mFirstHour > 0 && mFirstHour < 8) { + if (daynum < mEarliestStartHour!!.size && daynum >= 0 && mMaxAlldayEvents > 0 && + mEarliestStartHour!![daynum] > mSelectionHour && + mFirstHour > 0 && mFirstHour < 8) { mPrevSelectedEvent = null mSelectionAllday = true setSelectedHour(mFirstHour + 1) @@ -1296,8 +1349,8 @@ class DayView( mLastReloadMillis = 0 } - private val mCancelCallback: Runnable = object : Runnable() { - fun run() { + private val mCancelCallback: Runnable = object : Runnable { + override fun run() { clearCachedEvents() } } @@ -1331,34 +1384,35 @@ class DayView( mLastReloadMillis = millis // load events in the background -// mContext.startProgressSpinner(); + // mContext.startProgressSpinner(); val events: ArrayList = ArrayList() - mEventLoader.loadEventsInBackground(mNumDays, events, mFirstJulianDay, object : Runnable() { - fun run() { + mEventLoader.loadEventsInBackground(mNumDays, events as ArrayList, mFirstJulianDay, + object : Runnable { + override fun run() { val fadeinEvents = mFirstJulianDay != mLoadedFirstJulianDay mEvents = events mLoadedFirstJulianDay = mFirstJulianDay if (mAllDayEvents == null) { mAllDayEvents = ArrayList() } else { - mAllDayEvents.clear() + mAllDayEvents?.clear() } // Create a shorter array for all day events for (e in events) { if (e.drawAsAllday()) { - mAllDayEvents.add(e) + mAllDayEvents?.add(e) } } // New events, new layouts - if (mLayouts == null || mLayouts!!.size < events.size()) { - mLayouts = arrayOfNulls(events.size()) + if (mLayouts == null || mLayouts!!.size < events.size) { + mLayouts = arrayOfNulls(events.size) } else { Arrays.fill(mLayouts, null) } - if (mAllDayLayouts == null || mAllDayLayouts!!.size < mAllDayEvents.size()) { - mAllDayLayouts = arrayOfNulls(events.size()) + if (mAllDayLayouts == null || mAllDayLayouts!!.size < mAllDayEvents!!.size) { + mAllDayLayouts = arrayOfNulls(events.size) } else { Arrays.fill(mAllDayLayouts, null) } @@ -1372,9 +1426,9 @@ class DayView( if (mEventsCrossFadeAnimation == null) { mEventsCrossFadeAnimation = ObjectAnimator.ofInt(this@DayView, "EventsAlpha", 0, 255) - mEventsCrossFadeAnimation.setDuration(EVENTS_CROSS_FADE_DURATION) + mEventsCrossFadeAnimation?.setDuration(EVENTS_CROSS_FADE_DURATION.toLong()) } - mEventsCrossFadeAnimation.start() + mEventsCrossFadeAnimation?.start() } else { invalidate() } @@ -1391,7 +1445,7 @@ class DayView( fun stopEventsAnimation() { if (mEventsCrossFadeAnimation != null) { - mEventsCrossFadeAnimation.cancel() + mEventsCrossFadeAnimation?.cancel() } mEventsAlpha = 255 } @@ -1411,7 +1465,7 @@ class DayView( // the earliest event in each day. var maxAllDayEvents = 0 val events: ArrayList = mEvents - val len: Int = events.size() + val len: Int = events.size // Num of all-day-events on each day. val eventsCount = IntArray(mLastJulianDay - mFirstJulianDay + 1) Arrays.fill(eventsCount, 0) @@ -1441,23 +1495,23 @@ class DayView( } var day = daynum while (durationDays > 0) { - mHasAllDayEvent[day] = true + mHasAllDayEvent!![day] = true day++ durationDays-- } } else { var daynum: Int = event.startDay - mFirstJulianDay var hour: Int = event.startTime / 60 - if (daynum >= 0 && hour < mEarliestStartHour[daynum]) { - mEarliestStartHour[daynum] = hour + if (daynum >= 0 && hour < mEarliestStartHour!![daynum]) { + mEarliestStartHour!![daynum] = hour } // Also check the end hour in case the event spans more than // one day. daynum = event.endDay - mFirstJulianDay hour = event.endTime / 60 - if (daynum < mNumDays && hour < mEarliestStartHour[daynum]) { - mEarliestStartHour[daynum] = hour + if (daynum < mNumDays && hour < mEarliestStartHour!![daynum]) { + mEarliestStartHour!![daynum] = hour } } } @@ -1466,7 +1520,7 @@ class DayView( } @Override - protected fun onDraw(canvas: Canvas) { + protected override fun onDraw(canvas: Canvas) { if (mRemeasure) { remeasure(getWidth(), getHeight()) mRemeasure = false @@ -1474,7 +1528,7 @@ class DayView( canvas.save() val yTranslate = (-mViewStartY + DAY_HEADER_HEIGHT + mAlldayHeight).toFloat() // offset canvas by the current drag and header position - canvas.translate(-mViewStartX, yTranslate) + canvas.translate(-mViewStartX.toFloat(), yTranslate) // clip to everything below the allDay area val dest: Rect = mDestRect dest.top = (mFirstCell - yTranslate).toInt() @@ -1504,12 +1558,12 @@ class DayView( nextView.mTouchMode = TOUCH_MODE_INITIAL_STATE nextView.onDraw(canvas) // Move it back for this view - canvas.translate(-xTranslate, 0) + canvas.translate(-xTranslate, 0f) } else { // If we drew another view we already translated it back // If we didn't draw another view we should be at the edge of the // screen - canvas.translate(mViewStartX, -yTranslate) + canvas.translate(mViewStartX.toFloat(), -yTranslate) } // Draw the fixed areas (that don't scroll) directly to the canvas. @@ -1522,17 +1576,17 @@ class DayView( // Draw overscroll glow if (!mEdgeEffectTop.isFinished()) { if (DAY_HEADER_HEIGHT != 0) { - canvas.translate(0, DAY_HEADER_HEIGHT) + canvas.translate(0f, DAY_HEADER_HEIGHT.toFloat()) } if (mEdgeEffectTop.draw(canvas)) { invalidate() } if (DAY_HEADER_HEIGHT != 0) { - canvas.translate(0, -DAY_HEADER_HEIGHT) + canvas.translate(0f, -DAY_HEADER_HEIGHT.toFloat()) } } if (!mEdgeEffectBottom.isFinished()) { - canvas.rotate(180, mViewWidth / 2, mViewHeight / 2) + canvas.rotate(180f, mViewWidth.toFloat() / 2f, mViewHeight.toFloat() / 2f) if (mEdgeEffectBottom.draw(canvas)) { invalidate() } @@ -1581,7 +1635,7 @@ class DayView( p.setStyle(Style.FILL) p.setColor(mCalendarGridLineInnerHorizontalColor) p.setStrokeWidth(GRID_LINE_INNER_WIDTH) - canvas.drawLine(GRID_LINE_LEFT_MARGIN, y, right, y, p) + canvas.drawLine(GRID_LINE_LEFT_MARGIN, y.toFloat(), right.toFloat(), y.toFloat(), p) p.setAntiAlias(true) } @@ -1649,7 +1703,7 @@ class DayView( p.setTypeface(mBold) p.setTextAlign(Paint.Align.RIGHT) var cell = mFirstJulianDay - val dayNames: Array + val dayNames: Array? dayNames = if (mDateStrWidth < mCellWidth) { mDayStrs } else { @@ -1678,7 +1732,7 @@ class DayView( } } p.setColor(color) - drawDayHeader(dayNames[dayOfWeek], day, cell, canvas, p) + drawDayHeader(dayNames!![dayOfWeek], day, cell, canvas, p) day++ cell++ } @@ -1696,18 +1750,22 @@ class DayView( text = mPmString } var y = mFirstCell + mFirstHourOffset + 2 * mHoursTextHeight + HOUR_GAP - canvas.drawText(text, HOURS_LEFT_MARGIN, y, p) + canvas.drawText(text as String, HOURS_LEFT_MARGIN.toFloat(), y.toFloat(), p) if (mFirstHour < 12 && mFirstHour + mNumHours > 12) { // Also draw the "PM" text = mPmString y = - mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP) + 2 * mHoursTextHeight + HOUR_GAP - canvas.drawText(text, HOURS_LEFT_MARGIN, y, p) + mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP) + + 2 * mHoursTextHeight + HOUR_GAP + canvas.drawText(text as String, HOURS_LEFT_MARGIN.toFloat(), y.toFloat(), p) } } private fun drawCurrentTimeLine( - r: Rect, day: Int, top: Int, canvas: Canvas, + r: Rect, + day: Int, + top: Int, + canvas: Canvas, p: Paint ) { r.left = computeDayLeftPosition(day) - CURRENT_TIME_LINE_SIDE_BUFFER + 1 @@ -1746,7 +1804,8 @@ class DayView( // If this is today if (cell == mTodayJulianDay) { val lineY: Int = - mCurrentTime.hour * (mCellHeight + HOUR_GAP) + mCurrentTime.minute * mCellHeight / 60 + 1 + mCurrentTime!!.hour * (mCellHeight + HOUR_GAP) + mCurrentTime!!.minute * + mCellHeight / 60 + 1 // And the current time shows up somewhere on the screen if (lineY >= mViewStartY && lineY < mViewStartY + mViewHeight - 2) { @@ -1764,8 +1823,8 @@ class DayView( setupHourTextPaint(p) var y = HOUR_GAP + mHoursTextHeight + HOURS_TOP_MARGIN for (i in 0..23) { - val time = mHourStrs[i] - canvas.drawText(time, HOURS_LEFT_MARGIN, y, p) + val time = mHourStrs!![i] + canvas.drawText(time, HOURS_LEFT_MARGIN.toFloat(), y.toFloat(), p) y += mCellHeight + HOUR_GAP } } @@ -1780,14 +1839,14 @@ class DayView( private fun drawDayHeader(dayStr: String?, day: Int, cell: Int, canvas: Canvas, p: Paint) { var dateNum = mFirstVisibleDate + day - val x: Int + var x: Int if (dateNum > mMonthLength) { dateNum -= mMonthLength } p.setAntiAlias(true) val todayIndex = mTodayJulianDay - mFirstJulianDay // Draw day of the month - val dateNumStr: String = String.valueOf(dateNum) + val dateNumStr: String = dateNum.toString() if (mNumDays > 1) { val y = (DAY_HEADER_HEIGHT - DAY_HEADER_BOTTOM_MARGIN).toFloat() @@ -1796,29 +1855,28 @@ class DayView( p.setTextAlign(Align.RIGHT) p.setTextSize(DATE_HEADER_FONT_SIZE) p.setTypeface(if (todayIndex == day) mBold else Typeface.DEFAULT) - canvas.drawText(dateNumStr, x, y, p) + canvas.drawText(dateNumStr as String, x.toFloat(), y, p) // Draw day of the week - x -= p.measureText(" $dateNumStr") + x -= (p.measureText(" $dateNumStr")).toInt() p.setTextSize(DAY_HEADER_FONT_SIZE) p.setTypeface(Typeface.DEFAULT) - canvas.drawText(dayStr, x, y, p) + canvas.drawText(dayStr as String, x.toFloat(), y, p) } else { val y = (ONE_DAY_HEADER_HEIGHT - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN).toFloat() p.setTextAlign(Align.LEFT) - // Draw day of the week x = computeDayLeftPosition(day) + DAY_HEADER_ONE_DAY_LEFT_MARGIN p.setTextSize(DAY_HEADER_FONT_SIZE) p.setTypeface(Typeface.DEFAULT) - canvas.drawText(dayStr, x, y, p) + canvas.drawText(dayStr as String, x.toFloat(), y, p) // Draw day of the month - x += p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN + x += (p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN).toInt() p.setTextSize(DATE_HEADER_FONT_SIZE) p.setTypeface(if (todayIndex == day) mBold else Typeface.DEFAULT) - canvas.drawText(dateNumStr, x, y, p) + canvas.drawText(dateNumStr, x.toFloat(), y, p) } } @@ -1887,7 +1945,8 @@ class DayView( if (mNumDays == 1 && todayIndex == 0) { // Draw a white background for the time later than current time var lineY: Int = - mCurrentTime.hour * (mCellHeight + HOUR_GAP) + mCurrentTime.minute * mCellHeight / 60 + 1 + mCurrentTime!!.hour * (mCellHeight + HOUR_GAP) + mCurrentTime!!.minute * + mCellHeight / 60 + 1 if (lineY < mViewStartY + mViewHeight) { lineY = Math.max(lineY, mViewStartY) r.left = mHoursWidth @@ -1900,7 +1959,8 @@ class DayView( } else if (todayIndex >= 0 && todayIndex < mNumDays) { // Draw today with a white background for the time later than current time var lineY: Int = - mCurrentTime.hour * (mCellHeight + HOUR_GAP) + mCurrentTime.minute * mCellHeight / 60 + 1 + mCurrentTime!!.hour * (mCellHeight + HOUR_GAP) + mCurrentTime!!.minute * + mCellHeight / 60 + 1 if (lineY < mViewStartY + mViewHeight) { lineY = Math.max(lineY, mViewStartY) r.left = computeDayLeftPosition(todayIndex) + 1 @@ -1989,7 +2049,10 @@ class DayView( * Return the layout for a numbered event. Create it if not already existing */ private fun getEventLayout( - layouts: Array?, i: Int, event: Event, paint: Paint, + layouts: Array?, + i: Int, + event: Event, + paint: Paint, r: Rect ): StaticLayout? { if (i < 0 || i >= layouts!!.size) { @@ -2003,15 +2066,16 @@ class DayView( val bob = SpannableStringBuilder() if (event.title != null) { // MAX - 1 since we add a space - bob.append(drawTextSanitizer(event.title.toString(), MAX_EVENT_TEXT_LEN - 1)) - bob.setSpan(StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length(), 0) + bob.append(drawTextSanitizer(event.title.toString(), + MAX_EVENT_TEXT_LEN - 1)) + bob.setSpan(StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length, 0) bob.append(' ') } if (event.location != null) { bob.append( drawTextSanitizer( event.location.toString(), - MAX_EVENT_TEXT_LEN - bob.length() + MAX_EVENT_TEXT_LEN - bob.length ) ) } @@ -2021,7 +2085,8 @@ class DayView( paint.setColor(mEventTextColor) paint.setAlpha(Utils.DECLINED_EVENT_TEXT_ALPHA) } - Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED, Attendees.ATTENDEE_STATUS_TENTATIVE -> paint.setColor( + Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED, + Attendees.ATTENDEE_STATUS_TENTATIVE -> paint.setColor( mEventTextColor ) else -> paint.setColor(mEventTextColor) @@ -2029,7 +2094,7 @@ class DayView( // Leave a one pixel boundary on the left and right of the rectangle for the event layout = StaticLayout( - bob, 0, bob.length(), TextPaint(paint), r.width(), + bob, 0, bob.length, TextPaint(paint), r.width(), Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null, r.width() ) layouts[i] = layout @@ -2052,16 +2117,16 @@ class DayView( x = mHoursWidth.toFloat() p.setStrokeWidth(GRID_LINE_INNER_WIDTH) // Line bounding the top of the all day area - mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN - mLines[linesIndex++] = startY - mLines[linesIndex++] = computeDayLeftPosition(mNumDays).toFloat() - mLines[linesIndex++] = startY + mLines!![linesIndex++] = GRID_LINE_LEFT_MARGIN + mLines!![linesIndex++] = startY + mLines!![linesIndex++] = computeDayLeftPosition(mNumDays).toFloat() + mLines!![linesIndex++] = startY for (day in 0..mNumDays) { x = computeDayLeftPosition(day).toFloat() - mLines[linesIndex++] = x - mLines[linesIndex++] = startY - mLines[linesIndex++] = x - mLines[linesIndex++] = stopY + mLines!![linesIndex++] = x + mLines!![linesIndex++] = startY + mLines!![linesIndex++] = x + mLines!![linesIndex++] = stopY } p.setAntiAlias(false) canvas.drawLines(mLines, 0, linesIndex, p) @@ -2069,7 +2134,7 @@ class DayView( val y = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN val lastDay = firstDay + numDays - 1 val events: ArrayList? = mAllDayEvents - val numEvents: Int = events.size() + val numEvents: Int = events!!.size // Whether or not we should draw the more events text var hasMoreEvents = false // size of the allDay area @@ -2080,7 +2145,8 @@ class DayView( var allDayEventClip = DAY_HEADER_HEIGHT + mAlldayHeight + ALLDAY_TOP_MARGIN // The number of events that weren't drawn in each day mSkippedAlldayEvents = IntArray(numDays) - if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount && !mShowAllAllDayEvents && mAnimateDayHeight == 0) { + if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount && + !mShowAllAllDayEvents && mAnimateDayHeight == 0) { // We draw one fewer event than will fit so that more events text // can be drawn numRectangles = (mMaxUnexpandedAlldayEventCount - 1).toFloat() @@ -2094,7 +2160,7 @@ class DayView( var alpha: Int = eventTextPaint.getAlpha() eventTextPaint.setAlpha(mEventsAlpha) for (i in 0 until numEvents) { - val event: Event = events.get(i) + val event: Event = events!!.get(i) var startDay: Int = event.startDay var endDay: Int = event.endDay if (startDay > lastDay || endDay < firstDay) { @@ -2109,7 +2175,8 @@ class DayView( val startIndex = startDay - firstDay val endIndex = endDay - firstDay var height = - if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) mAnimateDayEventHeight.toFloat() else drawHeight / numRectangles + if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) + mAnimateDayEventHeight.toFloat() else drawHeight / numRectangles // Prevent a single event from getting too big if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) { @@ -2118,8 +2185,8 @@ class DayView( // Leave a one-pixel space between the vertical day lines and the // event rectangle. - event.left = computeDayLeftPosition(startIndex) - event.right = computeDayLeftPosition(endIndex + 1) - DAY_GAP + event.left = computeDayLeftPosition(startIndex).toFloat() + event.right = computeDayLeftPosition(endIndex + 1).toFloat() - DAY_GAP event.top = y + height * event.getColumn() event.bottom = event.top + height - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { @@ -2134,12 +2201,12 @@ class DayView( incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex) continue } - event.bottom = allDayEventClip + event.bottom = allDayEventClip.toFloat() } } val r: Rect = drawEventRect( - event, canvas, p, eventTextPaint, event.top as Int, - event.bottom as Int + event, canvas, p, eventTextPaint, event.top.toInt(), + event.bottom.toInt() ) setupAllDayTextRect(r) val layout: StaticLayout? = getEventLayout(mAllDayLayouts, i, event, eventTextPaint, r) @@ -2158,7 +2225,7 @@ class DayView( alpha = p.getAlpha() p.setAlpha(mEventsAlpha) p.setColor(mMoreAlldayEventsTextAlpha shl 24 and mMoreEventsTextColor) - for (i in mSkippedAlldayEvents.indices) { + for (i in mSkippedAlldayEvents!!.indices) { if (mSkippedAlldayEvents!![i] > 0) { drawMoreAlldayEvents(canvas, mSkippedAlldayEvents!![i], i, p) } @@ -2189,15 +2256,15 @@ class DayView( // Draws the "box +n" text for hidden allday events protected fun drawMoreAlldayEvents(canvas: Canvas, remainingEvents: Int, day: Int, p: Paint) { var x = computeDayLeftPosition(day) + EVENT_ALL_DAY_TEXT_LEFT_MARGIN - var y = (mAlldayHeight - .5f * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - (.5f - * EVENT_SQUARE_WIDTH) + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN).toInt() + var y = (mAlldayHeight - .5f * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - (.5f * + EVENT_SQUARE_WIDTH) + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN).toInt() val r: Rect = mRect r.top = y r.left = x r.bottom = y + EVENT_SQUARE_WIDTH r.right = x + EVENT_SQUARE_WIDTH p.setColor(mMoreEventsTextColor) - p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH) + p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH.toFloat()) p.setStyle(Style.STROKE) p.setAntiAlias(false) canvas.drawRect(r, p) @@ -2208,11 +2275,11 @@ class DayView( mResources.getQuantityString(R.plurals.month_more_events, remainingEvents) y += EVENT_SQUARE_WIDTH x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING - canvas.drawText(String.format(text, remainingEvents), x, y, p) + canvas.drawText(String.format(text, remainingEvents), x.toFloat(), y.toFloat(), p) } private fun computeAllDayNeighbors() { - val len: Int = mSelectedEvents.size() + val len: Int = mSelectedEvents.size if (len == 0 || mSelectedEvent != null) { return } @@ -2234,8 +2301,8 @@ class DayView( // Find the event in the same row as the previously selected all-day // event, if any. var startPosition = -1 - if (mPrevSelectedEvent != null && mPrevSelectedEvent.drawAsAllday()) { - startPosition = mPrevSelectedEvent.getColumn() + if (mPrevSelectedEvent != null && mPrevSelectedEvent!!.drawAsAllday()) { + startPosition = mPrevSelectedEvent?.getColumn() as Int } var maxPosition = -1 var startEvent: Event? = null @@ -2282,7 +2349,7 @@ class DayView( selectionArea.left = left selectionArea.right = selectionArea.left + cellWidth val events: ArrayList = mEvents - val numEvents: Int = events.size() + val numEvents: Int = events.size val geometry: EventGeometry = mEventGeometry val viewEndY = mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight val alpha: Int = eventTextPaint.getAlpha() @@ -2297,8 +2364,8 @@ class DayView( if (event.bottom < mViewStartY || event.top > viewEndY) { continue } - if (date == mSelectionDay && !mSelectionAllday && mComputeSelectedEvents - && geometry.eventIntersectsSelection(event, selectionArea) + if (date == mSelectionDay && !mSelectionAllday && mComputeSelectedEvents && + geometry.eventIntersectsSelection(event, selectionArea) ) { mSelectedEvents.add(event) } @@ -2324,15 +2391,19 @@ class DayView( } private fun drawEventRect( - event: Event, canvas: Canvas, p: Paint, eventTextPaint: Paint, - visibleTop: Int, visibleBot: Int + event: Event, + canvas: Canvas, + p: Paint, + eventTextPaint: Paint, + visibleTop: Int, + visibleBot: Int ): Rect { // Draw the Event Rect val r: Rect = mRect - r.top = Math.max(event.top as Int + EVENT_RECT_TOP_MARGIN, visibleTop) - r.bottom = Math.min(event.bottom as Int - EVENT_RECT_BOTTOM_MARGIN, visibleBot) - r.left = event.left as Int + EVENT_RECT_LEFT_MARGIN - r.right = event.right + r.top = Math.max(event.top.toInt() + EVENT_RECT_TOP_MARGIN, visibleTop) + r.bottom = Math.min(event.bottom.toInt() - EVENT_RECT_BOTTOM_MARGIN, visibleBot) + r.left = event.left.toInt() + EVENT_RECT_LEFT_MARGIN + r.right = event.right.toInt() var color: Int = event.color when (event.selfAttendeeStatus) { Attendees.ATTENDEE_STATUS_INVITED -> if (event !== mClickedEvent) { @@ -2344,22 +2415,23 @@ class DayView( } p.setStyle(Style.FILL_AND_STROKE) } - Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED, Attendees.ATTENDEE_STATUS_TENTATIVE -> p.setStyle( + Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED, + Attendees.ATTENDEE_STATUS_TENTATIVE -> p.setStyle( Style.FILL_AND_STROKE ) else -> p.setStyle(Style.FILL_AND_STROKE) } p.setAntiAlias(false) - val floorHalfStroke = Math.floor(EVENT_RECT_STROKE_WIDTH / 2.0f) as Int - val ceilHalfStroke = Math.ceil(EVENT_RECT_STROKE_WIDTH / 2.0f) as Int - r.top = Math.max(event.top as Int + EVENT_RECT_TOP_MARGIN + floorHalfStroke, visibleTop) + val floorHalfStroke = Math.floor(EVENT_RECT_STROKE_WIDTH.toDouble() / 2.0).toInt() + val ceilHalfStroke = Math.ceil(EVENT_RECT_STROKE_WIDTH.toDouble() / 2.0).toInt() + r.top = Math.max(event.top.toInt() + EVENT_RECT_TOP_MARGIN + floorHalfStroke, visibleTop) r.bottom = Math.min( - event.bottom as Int - EVENT_RECT_BOTTOM_MARGIN - ceilHalfStroke, + event.bottom.toInt() - EVENT_RECT_BOTTOM_MARGIN - ceilHalfStroke, visibleBot ) r.left += floorHalfStroke r.right -= ceilHalfStroke - p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH) + p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH.toFloat()) p.setColor(color) val alpha: Int = p.getAlpha() p.setAlpha(mEventsAlpha) @@ -2368,10 +2440,10 @@ class DayView( p.setStyle(Style.FILL) // Setup rect for drawEventText which follows - r.top = event.top as Int + EVENT_RECT_TOP_MARGIN - r.bottom = event.bottom as Int - EVENT_RECT_BOTTOM_MARGIN - r.left = event.left as Int + EVENT_RECT_LEFT_MARGIN - r.right = event.right as Int - EVENT_RECT_RIGHT_MARGIN + r.top = event.top.toInt() + EVENT_RECT_TOP_MARGIN + r.bottom = event.bottom.toInt() - EVENT_RECT_BOTTOM_MARGIN + r.left = event.left.toInt() + EVENT_RECT_LEFT_MARGIN + r.right = event.right.toInt() - EVENT_RECT_RIGHT_MARGIN return r } @@ -2384,7 +2456,7 @@ class DayView( var string = string val m: Matcher = drawTextSanitizerFilter.matcher(string) string = m.replaceAll(",") - var len: Int = string.length() + var len: Int = string.length if (maxEventTextLen <= 0) { string = "" len = 0 @@ -2396,8 +2468,12 @@ class DayView( } private fun drawEventText( - eventLayout: StaticLayout?, rect: Rect, canvas: Canvas, top: Int, - bottom: Int, center: Boolean + eventLayout: StaticLayout?, + rect: Rect, + canvas: Canvas, + top: Int, + bottom: Int, + center: Boolean ) { // drawEmptyRect(canvas, rect, 0xFFFF00FF); // for debugging val width: Int = rect.right - rect.left @@ -2428,7 +2504,7 @@ class DayView( canvas.save() // canvas.translate(rect.left, rect.top + (rect.bottom - rect.top / 2)); val padding = if (center) (rect.bottom - rect.top - totalLineHeight) / 2 else 0 - canvas.translate(rect.left, rect.top + padding) + canvas.translate(rect.left.toFloat(), rect.top.toFloat() + padding) rect.left = 0 rect.right = width rect.top = 0 @@ -2449,9 +2525,9 @@ class DayView( mTouchMode = TOUCH_MODE_DOWN mViewStartX = 0 mOnFlingCalled = false - mHandler.removeCallbacks(mContinueScroll) - val x = ev.getX() as Int - val y = ev.getY() as Int + mHandler?.removeCallbacks(mContinueScroll) + val x = ev.getX().toInt() + val y = ev.getY().toInt() // Save selection information: we use setSelectionFromPosition to find the selected event // in order to show the "clicked" color. But since it is also setting the selected info @@ -2463,12 +2539,12 @@ class DayView( // If a time was selected (a blue selection box is visible) and the click location // is in the selected time, do not show a click on an event to prevent a situation // of both a selection and an event are clicked when they overlap. - val pressedSelected = (mSelectionMode != SELECTION_HIDDEN - && oldSelectionDay == mSelectionDay && oldSelectionHour == mSelectionHour) + val pressedSelected = (mSelectionMode != SELECTION_HIDDEN && + oldSelectionDay == mSelectionDay && oldSelectionHour == mSelectionHour) if (!pressedSelected && mSelectedEvent != null) { mSavedClickedEvent = mSelectedEvent mDownTouchTime = System.currentTimeMillis() - postDelayed(mSetClick, mOnDownDelay) + postDelayed(mSetClick, mOnDownDelay.toLong()) } else { eventClickCleanup() } @@ -2487,18 +2563,19 @@ class DayView( // Determine the starting height if (mAnimateDayHeight == 0) { mAnimateDayHeight = - if (mShowAllAllDayEvents) mAlldayHeight - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt() else mAlldayHeight + if (mShowAllAllDayEvents) mAlldayHeight - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt() + else mAlldayHeight } // Cancel current animations mCancellingAnimations = true if (mAlldayAnimator != null) { - mAlldayAnimator.cancel() + mAlldayAnimator?.cancel() } if (mAlldayEventAnimator != null) { - mAlldayEventAnimator.cancel() + mAlldayEventAnimator?.cancel() } if (mMoreAlldayEventsAnimator != null) { - mMoreAlldayEventsAnimator.cancel() + mMoreAlldayEventsAnimator?.cancel() } mCancellingAnimations = false // get new animators @@ -2512,16 +2589,18 @@ class DayView( ) // Set up delays and start the animators - mAlldayAnimator.setStartDelay(if (mShowAllAllDayEvents) ANIMATION_SECONDARY_DURATION else 0) - mAlldayAnimator.start() - mMoreAlldayEventsAnimator.setStartDelay(if (mShowAllAllDayEvents) 0 else ANIMATION_DURATION) - mMoreAlldayEventsAnimator.setDuration(ANIMATION_SECONDARY_DURATION) - mMoreAlldayEventsAnimator.start() + mAlldayAnimator?.setStartDelay(if (mShowAllAllDayEvents) ANIMATION_SECONDARY_DURATION + else 0) + mAlldayAnimator?.start() + mMoreAlldayEventsAnimator?.setStartDelay(if (mShowAllAllDayEvents) 0 + else ANIMATION_DURATION) + mMoreAlldayEventsAnimator?.setDuration(ANIMATION_SECONDARY_DURATION) + mMoreAlldayEventsAnimator?.start() if (mAlldayEventAnimator != null) { // This is the only animator that can return null, so check it mAlldayEventAnimator - .setStartDelay(if (mShowAllAllDayEvents) ANIMATION_SECONDARY_DURATION else 0) - mAlldayEventAnimator.start() + ?.setStartDelay(if (mShowAllAllDayEvents) ANIMATION_SECONDARY_DURATION else 0) + mAlldayEventAnimator?.start() } } @@ -2543,7 +2622,7 @@ class DayView( } else { mAnimateDayEventHeight = MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt() } - }// First calculate the absolute max height + } // First calculate the absolute max height // Now expand to fit but not beyond the absolute max // calculate the height of individual events in order to fit // if there's nothing to animate just return @@ -2576,9 +2655,7 @@ class DayView( ) animator.setDuration(ANIMATION_DURATION) return animator - }// when finished, set this to 0 to signify not animating// Calculate the absolute max height - // Find the desired height but don't exceed abs max - // calculate the current and desired heights + } // Set up the animator with the calculated values // Sets up an animator for changing the height of the allday area @@ -2594,7 +2671,8 @@ class DayView( // calculate the current and desired heights val currentHeight = if (mAnimateDayHeight != 0) mAnimateDayHeight else mAlldayHeight val desiredHeight = - if (mShowAllAllDayEvents) maxADHeight else (MAX_UNEXPANDED_ALLDAY_HEIGHT - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - 1).toInt() + if (mShowAllAllDayEvents) maxADHeight else (MAX_UNEXPANDED_ALLDAY_HEIGHT - + MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - 1).toInt() // Set up the animator with the calculated values val animator: ObjectAnimator = ObjectAnimator.ofInt( @@ -2604,7 +2682,7 @@ class DayView( animator.setDuration(ANIMATION_DURATION) animator.addListener(object : AnimatorListenerAdapter() { @Override - fun onAnimationEnd(animation: Animator?) { + override fun onAnimationEnd(animation: Animator?) { if (!mCancellingAnimations) { // when finished, set this to 0 to signify not animating mAnimateDayHeight = 0 @@ -2641,15 +2719,16 @@ class DayView( if (!mHandleActionUp || mScrolling) { return } - val x = ev.getX() as Int - val y = ev.getY() as Int + val x = ev.getX().toInt() + val y = ev.getY().toInt() val selectedDay = mSelectionDay val selectedHour = mSelectionHour if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { // check if the tap was in the allday expansion area val bottom = mFirstCell - if (x < mHoursWidth && y > DAY_HEADER_HEIGHT && y < DAY_HEADER_HEIGHT + mAlldayHeight - || !mShowAllAllDayEvents && mAnimateDayHeight == 0 && y < bottom && y >= bottom - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT + if (x < mHoursWidth && y > DAY_HEADER_HEIGHT && y < DAY_HEADER_HEIGHT + mAlldayHeight || + !mShowAllAllDayEvents && mAnimateDayHeight == 0 && y < bottom && y >= bottom - + MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT ) { doExpandAllDayClick() return @@ -2663,30 +2742,30 @@ class DayView( selectedTime.hour = mSelectionHour selectedTime.normalize(true /* ignore isDst */) mController.sendEvent( - this, EventType.GO_TO, null, null, selectedTime, -1, + this as? Object, EventType.GO_TO, null, null, selectedTime, -1, ViewType.DAY, CalendarController.EXTRA_GOTO_DATE, null, null ) } return } val hasSelection = mSelectionMode != SELECTION_HIDDEN - val pressedSelected = ((hasSelection || mTouchExplorationEnabled) - && selectedDay == mSelectionDay && selectedHour == mSelectionHour) + val pressedSelected = ((hasSelection || mTouchExplorationEnabled) && + selectedDay == mSelectionDay && selectedHour == mSelectionHour) if (mSelectedEvent != null) { // If the tap is on an event, launch the "View event" view if (mIsAccessibilityEnabled) { - mAccessibilityMgr.interrupt() + mAccessibilityMgr?.interrupt() } mSelectionMode = SELECTION_HIDDEN - var yLocation = ((mSelectedEvent.top + mSelectedEvent.bottom) / 2) as Int + var yLocation = ((mSelectedEvent!!.top + mSelectedEvent!!.bottom) / 2) as Int // Y location is affected by the position of the event in the scrolling // view (mViewStartY) and the presence of all day events (mFirstCell) - if (!mSelectedEvent.allDay) { + if (!mSelectedEvent!!.allDay) { yLocation += mFirstCell - mViewStartY } mClickedYLocation = yLocation val clearDelay: Long = CLICK_DISPLAY_DURATION + mOnDownDelay - - (System.currentTimeMillis() - mDownTouchTime) + (System.currentTimeMillis() - mDownTouchTime) if (clearDelay > 0) { this.postDelayed(mClearClick, clearDelay) } else { @@ -2706,8 +2785,8 @@ class DayView( if (mStartingSpanY != 0f) { return } - val x = ev.getX() as Int - val y = ev.getY() as Int + val x = ev.getX().toInt() + val y = ev.getY().toInt() val validPosition = setSelectionFromPosition(x, y, false) if (!validPosition) { // return if the touch wasn't on an area of concern @@ -2731,8 +2810,8 @@ class DayView( val focusY = getAverageY(e2) if (mRecalCenterHour) { // Calculate the hour that correspond to the average of the Y touch points - mGestureCenterHour = ((mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) - / (mCellHeight + DAY_GAP)) + mGestureCenterHour = ((mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) / + (mCellHeight + DAY_GAP)) mRecalCenterHour = false } @@ -2771,8 +2850,8 @@ class DayView( if (mTouchMode and TOUCH_MODE_VSCROLL != 0) { // Calculate the top of the visible region in the calendar grid. // Increasing/decrease this will scroll the calendar grid up/down. - mViewStartY = ((mGestureCenterHour * (mCellHeight + DAY_GAP) - - focusY) + DAY_HEADER_HEIGHT + mAlldayHeight).toInt() + mViewStartY = ((mGestureCenterHour * (mCellHeight + DAY_GAP) - + focusY) + DAY_HEADER_HEIGHT + mAlldayHeight).toInt() // If dragging while already at the end, do a glow val pulledToY = (mScrollStartY + deltaY).toInt() @@ -2796,8 +2875,8 @@ class DayView( } if (mRecalCenterHour) { // Calculate the hour that correspond to the average of the Y touch points - mGestureCenterHour = ((mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) - / (mCellHeight + DAY_GAP)) + mGestureCenterHour = ((mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) / + (mCellHeight + DAY_GAP)) mRecalCenterHour = false } computeFirstHour() @@ -2818,15 +2897,15 @@ class DayView( } private fun cancelAnimation() { - val `in`: Animation = mViewSwitcher.getInAnimation() + val `in`: Animation? = mViewSwitcher?.getInAnimation() if (`in` != null) { // cancel() doesn't terminate cleanly. - `in`.scaleCurrentDuration(0) + `in`?.scaleCurrentDuration(0f) } - val out: Animation = mViewSwitcher.getOutAnimation() + val out: Animation? = mViewSwitcher?.getOutAnimation() if (out != null) { // cancel() doesn't terminate cleanly. - out.scaleCurrentDuration(0) + out?.scaleCurrentDuration(0f) } } @@ -2840,7 +2919,7 @@ class DayView( // initNextView(deltaX); mTouchMode = TOUCH_MODE_INITIAL_STATE if (DEBUG) Log.d(TAG, "doFling: velocityX $velocityX") - val deltaX = e2.getX() as Int - e1.getX() as Int + val deltaX = e2.getX().toInt() - e1.getX().toInt() switchViews(deltaX < 0, mViewStartX.toFloat(), mViewWidth.toFloat(), velocityX) mViewStartX = 0 return @@ -2872,25 +2951,25 @@ class DayView( } else if (velocityY < 0 && mViewStartY != mMaxViewStartY) { mCallEdgeEffectOnAbsorb = true } - mHandler.post(mContinueScroll) + mHandler?.post(mContinueScroll) } private fun initNextView(deltaX: Int): Boolean { // Change the view to the previous day or week val view = mViewSwitcher.getNextView() as DayView val date: Time? = view.mBaseDate - date.set(mBaseDate) + date?.set(mBaseDate) val switchForward: Boolean if (deltaX > 0) { - date.monthDay -= mNumDays - view.selectedDay = mSelectionDay - mNumDays + date!!.monthDay -= mNumDays + view.setSelectedDay(mSelectionDay - mNumDays) switchForward = false } else { - date.monthDay += mNumDays - view.selectedDay = mSelectionDay + mNumDays + date!!.monthDay += mNumDays + view.setSelectedDay(mSelectionDay + mNumDays) switchForward = true } - date.normalize(true /* ignore isDst */) + date?.normalize(true /* ignore isDst */) initView(view) view.layout(getLeft(), getTop(), getRight(), getBottom()) view.reloadEvents() @@ -2898,26 +2977,28 @@ class DayView( } // ScaleGestureDetector.OnScaleGestureListener - fun onScaleBegin(detector: ScaleGestureDetector): Boolean { + override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { mHandleActionUp = false val gestureCenterInPixels: Float = detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight mGestureCenterHour = (mViewStartY + gestureCenterInPixels) / (mCellHeight + DAY_GAP) - mStartingSpanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY())) + mStartingSpanY = Math.max(MIN_Y_SPAN.toFloat(), + Math.abs(detector.getCurrentSpanY().toFloat())) mCellHeightBeforeScaleGesture = mCellHeight if (DEBUG_SCALING) { val ViewStartHour = mViewStartY / (mCellHeight + DAY_GAP).toFloat() Log.d( - TAG, "onScaleBegin: mGestureCenterHour:" + mGestureCenterHour - + "\tViewStartHour: " + ViewStartHour + "\tmViewStartY:" + mViewStartY - + "\tmCellHeight:" + mCellHeight + " SpanY:" + detector.getCurrentSpanY() + TAG, "onScaleBegin: mGestureCenterHour:" + mGestureCenterHour + + "\tViewStartHour: " + ViewStartHour + "\tmViewStartY:" + mViewStartY + + "\tmCellHeight:" + mCellHeight + " SpanY:" + detector.getCurrentSpanY() ) } return true } // ScaleGestureDetector.OnScaleGestureListener - fun onScale(detector: ScaleGestureDetector): Boolean { - val spanY: Float = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY())) + override fun onScale(detector: ScaleGestureDetector): Boolean { + val spanY: Float = Math.max(MIN_Y_SPAN.toFloat(), + Math.abs(detector.getCurrentSpanY().toFloat())) mCellHeight = (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY).toInt() if (mCellHeight < mMinCellHeight) { // If mStartingSpanY is too small, even a small increase in the @@ -2930,25 +3011,25 @@ class DayView( mCellHeight = MAX_CELL_HEIGHT mCellHeightBeforeScaleGesture = MAX_CELL_HEIGHT } - val gestureCenterInPixels = detector.getFocusY() as Int - DAY_HEADER_HEIGHT - mAlldayHeight + val gestureCenterInPixels = detector.getFocusY().toInt() - DAY_HEADER_HEIGHT - mAlldayHeight mViewStartY = (mGestureCenterHour * (mCellHeight + DAY_GAP)).toInt() - gestureCenterInPixels mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight if (DEBUG_SCALING) { val ViewStartHour = mViewStartY / (mCellHeight + DAY_GAP).toFloat() Log.d( - TAG, "onScale: mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: " - + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:" - + mCellHeight + " SpanY:" + detector.getCurrentSpanY() + TAG, "onScale: mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: " + + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:" + + mCellHeight + " SpanY:" + detector.getCurrentSpanY() ) } if (mViewStartY < 0) { mViewStartY = 0 - mGestureCenterHour = ((mViewStartY + gestureCenterInPixels) - / (mCellHeight + DAY_GAP).toFloat()) + mGestureCenterHour = ((mViewStartY + gestureCenterInPixels) / + (mCellHeight + DAY_GAP).toFloat()) } else if (mViewStartY > mMaxViewStartY) { mViewStartY = mMaxViewStartY - mGestureCenterHour = ((mViewStartY + gestureCenterInPixels) - / (mCellHeight + DAY_GAP).toFloat()) + mGestureCenterHour = ((mViewStartY + gestureCenterInPixels) / + (mCellHeight + DAY_GAP).toFloat()) } computeFirstHour() mRemeasure = true @@ -2957,7 +3038,7 @@ class DayView( } // ScaleGestureDetector.OnScaleGestureListener - fun onScaleEnd(detector: ScaleGestureDetector?) { + override fun onScaleEnd(detector: ScaleGestureDetector?) { mScrollStartY = mViewStartY mInitialScrollY = 0f mInitialScrollX = 0f @@ -2965,7 +3046,7 @@ class DayView( } @Override - fun onTouchEvent(ev: MotionEvent): Boolean { + override fun onTouchEvent(ev: MotionEvent): Boolean { val action: Int = ev.getAction() if (DEBUG) Log.e(TAG, "" + action + " ev.getPointerCount() = " + ev.getPointerCount()) if (ev.getActionMasked() === MotionEvent.ACTION_DOWN || @@ -2984,8 +3065,8 @@ class DayView( if (DEBUG) { Log.e( TAG, - "ACTION_DOWN ev.getDownTime = " + ev.getDownTime().toString() + " Cnt=" - + ev.getPointerCount() + "ACTION_DOWN ev.getDownTime = " + ev.getDownTime().toString() + " Cnt=" + + ev.getPointerCount() ) } val bottom = @@ -3086,7 +3167,7 @@ class DayView( } } - fun onCreateContextMenu(menu: ContextMenu, view: View?, menuInfo: ContextMenuInfo?) { + override fun onCreateContextMenu(menu: ContextMenu, view: View?, menuInfo: ContextMenuInfo?) { var item: MenuItem // If the trackball is held down, then the context menu pops up and @@ -3097,11 +3178,11 @@ class DayView( } val startMillis = selectedTimeInMillis val flags: Int = (DateUtils.FORMAT_SHOW_TIME - or DateUtils.FORMAT_CAP_NOON_MIDNIGHT - or DateUtils.FORMAT_SHOW_WEEKDAY) - val title: String = Utils.formatDateRange(mContext, startMillis, startMillis, flags) + or DateUtils.FORMAT_CAP_NOON_MIDNIGHT + or DateUtils.FORMAT_SHOW_WEEKDAY) + val title: String? = Utils.formatDateRange(mContext, startMillis, startMillis, flags) menu.setHeaderTitle(title) - mPopup.dismiss() + mPopup?.dismiss() } /** @@ -3137,7 +3218,7 @@ class DayView( day = mNumDays - 1 } day += mFirstJulianDay - selectedDay = day + setSelectedDay(day) if (y < DAY_HEADER_HEIGHT) { sendAccessibilityEventAsNeeded(false) return false @@ -3153,7 +3234,7 @@ class DayView( } else { setSelectedHour( mSelectionHour + - (adjustedY - mFirstHourOffset) / (mCellHeight + HOUR_GAP) + (adjustedY - mFirstHourOffset) / (mCellHeight + HOUR_GAP) ) } false @@ -3176,7 +3257,7 @@ class DayView( val date = mSelectionDay val cellWidth = mCellWidth var events: ArrayList? = mEvents - var numEvents: Int = events.size() + var numEvents: Int = events!!.size val left = computeDayLeftPosition(mSelectionDay - mFirstJulianDay) val top = 0 setSelectedEvent(null) @@ -3193,23 +3274,24 @@ class DayView( maxUnexpandedColumn-- } events = mAllDayEvents - numEvents = events.size() + numEvents = events!!.size for (i in 0 until numEvents) { - val event: Event = events.get(i) - if (!event.drawAsAllday() || - !mShowAllAllDayEvents && event.getColumn() >= maxUnexpandedColumn + val event: Event? = events?.get(i) + if (!event!!.drawAsAllday() || + !mShowAllAllDayEvents && event!!.getColumn() >= maxUnexpandedColumn ) { // Don't check non-allday events or events that aren't shown continue } - if (event.startDay <= mSelectionDay && event.endDay >= mSelectionDay) { + if (event!!.startDay <= mSelectionDay && event!!.endDay >= mSelectionDay) { val numRectangles = - if (mShowAllAllDayEvents) mMaxAlldayEvents.toFloat() else mMaxUnexpandedAlldayEventCount.toFloat() + if (mShowAllAllDayEvents) mMaxAlldayEvents.toFloat() + else mMaxUnexpandedAlldayEventCount.toFloat() var height = drawHeight / numRectangles if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) { height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT.toFloat() } - val eventTop: Float = yOffset + height * event.getColumn() + val eventTop: Float = yOffset + height * event?.getColumn() val eventBottom = eventTop + height if (eventTop < y && eventBottom > y) { // If the touch is inside the event rectangle, then @@ -3246,28 +3328,28 @@ class DayView( region.bottom = y + 10 val geometry: EventGeometry = mEventGeometry for (i in 0 until numEvents) { - val event: Event = events.get(i) + val event: Event? = events?.get(i) // Compute the event rectangle. - if (!geometry.computeEventRect(date, left, top, cellWidth, event)) { + if (!geometry.computeEventRect(date, left, top, cellWidth, event as Event)) { continue } // If the event intersects the selection region, then add it to // mSelectedEvents. - if (geometry.eventIntersectsSelection(event, region)) { - mSelectedEvents.add(event) + if (geometry.eventIntersectsSelection(event as Event, region)) { + mSelectedEvents.add(event as Event) } } // If there are any events in the selected region, then assign the // closest one to mSelectedEvent. - if (mSelectedEvents.size() > 0) { - val len: Int = mSelectedEvents.size() + if (mSelectedEvents.size > 0) { + val len: Int = mSelectedEvents.size var closestEvent: Event? = null var minDist = (mViewWidth + mViewHeight).toFloat() // some large distance for (index in 0 until len) { - val ev: Event = mSelectedEvents.get(index) - val dist: Float = geometry.pointToEvent(x, y, ev) + val ev: Event? = mSelectedEvents?.get(index) + val dist: Float = geometry.pointToEvent(x.toFloat(), y.toFloat(), ev as Event) if (dist < minDist) { minDist = dist closestEvent = ev @@ -3279,19 +3361,19 @@ class DayView( // event. They could be different if we touched on an empty hour // slot very close to an event in the previous hour slot. In // that case we will select the nearby event. - val startDay: Int = mSelectedEvent.startDay - val endDay: Int = mSelectedEvent.endDay + val startDay: Int = mSelectedEvent!!.startDay + val endDay: Int = mSelectedEvent!!.endDay if (mSelectionDay < startDay) { - selectedDay = startDay + setSelectedDay(startDay) } else if (mSelectionDay > endDay) { - selectedDay = endDay + setSelectedDay(endDay) } - val startHour: Int = mSelectedEvent.startTime / 60 + val startHour: Int = mSelectedEvent!!.startTime / 60 val endHour: Int - endHour = if (mSelectedEvent.startTime < mSelectedEvent.endTime) { - (mSelectedEvent.endTime - 1) / 60 + endHour = if (mSelectedEvent!!.startTime < mSelectedEvent!!.endTime) { + (mSelectedEvent!!.endTime - 1) / 60 } else { - mSelectedEvent.endTime / 60 + mSelectedEvent!!.endTime / 60 } if (mSelectionHour < startHour && mSelectionDay == startDay) { setSelectedHour(startHour) @@ -3305,7 +3387,7 @@ class DayView( // finger is lifted. Instead of stopping the scroll immediately, // the scroll continues to "free spin" and gradually slows down. private inner class ContinueScroll : Runnable { - fun run() { + override fun run() { mScrolling = mScrolling && mScroller.computeScrollOffset() if (!mScrolling || mPaused) { resetSelectedHour() @@ -3333,7 +3415,7 @@ class DayView( } } computeFirstHour() - mHandler.post(this) + mHandler?.post(this) invalidate() } } @@ -3344,13 +3426,13 @@ class DayView( fun cleanup() { // Protect against null-pointer exceptions if (mPopup != null) { - mPopup.dismiss() + mPopup?.dismiss() } mPaused = true mLastPopupEventID = INVALID_EVENT_ID if (mHandler != null) { - mHandler.removeCallbacks(mDismissPopup) - mHandler.removeCallbacks(mUpdateCurrentTime) + mHandler?.removeCallbacks(mDismissPopup) + mHandler?.removeCallbacks(mUpdateCurrentTime) } Utils.setSharedPreference( mContext, GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, @@ -3381,64 +3463,69 @@ class DayView( mSelectionHourForAccessibility = h } + private fun setSelectedDay(d: Int) { + mSelectionDay = d + mSelectionDayForAccessibility = d + } + /** * Restart the update timer */ fun restartCurrentTimeUpdates() { mPaused = false if (mHandler != null) { - mHandler.removeCallbacks(mUpdateCurrentTime) - mHandler.post(mUpdateCurrentTime) + mHandler?.removeCallbacks(mUpdateCurrentTime) + mHandler?.post(mUpdateCurrentTime) } } @Override - protected fun onDetachedFromWindow() { + protected override fun onDetachedFromWindow() { cleanup() super.onDetachedFromWindow() } internal inner class DismissPopup : Runnable { - fun run() { + override fun run() { // Protect against null-pointer exceptions if (mPopup != null) { - mPopup.dismiss() + mPopup?.dismiss() } } } internal inner class UpdateCurrentTime : Runnable { - fun run() { + override fun run() { val currentTime: Long = System.currentTimeMillis() - mCurrentTime.set(currentTime) - //% causes update to occur on 5 minute marks (11:10, 11:15, 11:20, etc.) + mCurrentTime?.set(currentTime) + // % causes update to occur on 5 minute marks (11:10, 11:15, 11:20, etc.) if (!mPaused) { - mHandler.postDelayed( - mUpdateCurrentTime, UPDATE_CURRENT_TIME_DELAY - - currentTime % UPDATE_CURRENT_TIME_DELAY + mHandler?.postDelayed( + mUpdateCurrentTime, UPDATE_CURRENT_TIME_DELAY - + currentTime % UPDATE_CURRENT_TIME_DELAY ) } - mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff) + mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime!!.gmtoff) invalidate() } } internal inner class CalendarGestureListener : GestureDetector.SimpleOnGestureListener() { @Override - fun onSingleTapUp(ev: MotionEvent): Boolean { + override fun onSingleTapUp(ev: MotionEvent): Boolean { if (DEBUG) Log.e(TAG, "GestureDetector.onSingleTapUp") doSingleTapUp(ev) return true } @Override - fun onLongPress(ev: MotionEvent) { + override fun onLongPress(ev: MotionEvent) { if (DEBUG) Log.e(TAG, "GestureDetector.onLongPress") doLongPress(ev) } @Override - fun onScroll( + override fun onScroll( e1: MotionEvent, e2: MotionEvent, distanceX: Float, @@ -3462,7 +3549,12 @@ class DayView( } @Override - fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { + override fun onFling( + e1: MotionEvent, + e2: MotionEvent, + velocityX: Float, + velocityY: Float + ): Boolean { var velocityY = velocityY if (DEBUG) Log.e(TAG, "GestureDetector.onFling") if (mTouchStartedInAlldayArea) { @@ -3477,7 +3569,7 @@ class DayView( } @Override - fun onDown(ev: MotionEvent): Boolean { + override fun onDown(ev: MotionEvent): Boolean { if (DEBUG) Log.e(TAG, "GestureDetector.onDown") doDown(ev) return true @@ -3485,12 +3577,12 @@ class DayView( } @Override - fun onLongClick(v: View?): Boolean { + override fun onLongClick(v: View?): Boolean { return true } private inner class ScrollInterpolator : Interpolator { - fun getInterpolation(t: Float): Float { + override fun getInterpolation(t: Float): Float { var t = t t -= 1.0f t = t * t * t * t * t + 1 @@ -3515,7 +3607,7 @@ class DayView( val distanceInfluenceForSnapDuration = distanceInfluenceForSnapDuration(distanceRatio) val distance = halfScreenSize + halfScreenSize * distanceInfluenceForSnapDuration velocity = Math.abs(velocity) - velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity) + velocity = Math.max(MINIMUM_SNAP_VELOCITY.toFloat(), velocity) /* * we want the page's snap velocity to approximately match the velocity @@ -3523,13 +3615,13 @@ class DayView( * the derivative of the scroll interpolator at zero, ie. 5. We use 6 to * make it a little slower. */ - val duration: Long = 6 * Math.round(1000 * Math.abs(distance / velocity)) + val duration: Long = 6L * Math.round(1000 * Math.abs(distance / velocity)) if (DEBUG) { Log.e( - TAG, "halfScreenSize:" + halfScreenSize + " delta:" + delta + " distanceRatio:" - + distanceRatio + " distance:" + distance + " velocity:" + velocity - + " duration:" + duration + " distanceInfluenceForSnapDuration:" - + distanceInfluenceForSnapDuration + TAG, "halfScreenSize:" + halfScreenSize + " delta:" + delta + " distanceRatio:" + + distanceRatio + " distance:" + distance + " velocity:" + velocity + + " duration:" + duration + " distanceInfluenceForSnapDuration:" + + distanceInfluenceForSnapDuration ) } return duration @@ -3545,8 +3637,8 @@ class DayView( private fun distanceInfluenceForSnapDuration(f: Float): Float { var f = f f -= 0.5f // center the values about 0. - f *= 0.3f * Math.PI / 2.0f - return Math.sin(f) + f *= (0.3f * Math.PI / 2.0f).toFloat() + return Math.sin(f.toDouble()).toFloat() } companion object { @@ -3555,7 +3647,7 @@ class DayView( private const val DEBUG_SCALING = false private const val PERIOD_SPACE = ". " private var mScale = 0f // Used for supporting different screen densities - private const val INVALID_EVENT_ID: Long = -1 //This is used for remembering a null event + private const val INVALID_EVENT_ID: Long = -1 // This is used for remembering a null event // Duration of the allday expansion private const val ANIMATION_DURATION: Long = 400 @@ -3580,8 +3672,8 @@ class DayView( private var MAX_CELL_HEIGHT = 150 private var MIN_Y_SPAN = 100 private val CALENDARS_PROJECTION = arrayOf( - Calendars._ID, // 0 - Calendars.CALENDAR_ACCESS_LEVEL, // 1 + Calendars._ID, // 0 + Calendars.CALENDAR_ACCESS_LEVEL, // 1 Calendars.OWNER_ACCOUNT ) private const val CALENDARS_INDEX_ACCESS_LEVEL = 1 @@ -3597,9 +3689,9 @@ class DayView( private const val ACCESS_LEVEL_EDIT = 2 private var mHorizontalSnapBackThreshold = 128 - //Update the current time line every five minutes if the window is left open that long + // Update the current time line every five minutes if the window is left open that long private const val UPDATE_CURRENT_TIME_DELAY = 300000 - private var mOnDownDelay: Int + private var mOnDownDelay = 0 protected var mStringBuilder: StringBuilder = StringBuilder(50) // TODO recreate formatter when locale changes @@ -3717,7 +3809,7 @@ class DayView( private var mFutureBgColor = 0 private var mFutureBgColorRes = 0 private var mBgColor = 0 - private var mNewEventHintColor: Int + private var mNewEventHintColor = 0 private var mCalendarHourLabelColor = 0 private var mMoreAlldayEventsTextAlpha = MORE_EVENTS_MAX_ALPHA private var mCellHeight = 0 // shared among all DayViews @@ -3786,37 +3878,39 @@ class DayView( init { mContext = context initAccessibilityVariables() - mResources = context.getResources() + mResources = context!!.getResources() mNewEventHintString = mResources.getString(R.string.day_view_new_event_hint) mNumDays = numDays DATE_HEADER_FONT_SIZE = - mResources.getDimension(R.dimen.date_header_text_size) as Int.toFloat() - DAY_HEADER_FONT_SIZE = mResources.getDimension(R.dimen.day_label_text_size) as Int.toFloat() - ONE_DAY_HEADER_HEIGHT = mResources.getDimension(R.dimen.one_day_header_height) - DAY_HEADER_BOTTOM_MARGIN = mResources.getDimension(R.dimen.day_header_bottom_margin) - EXPAND_ALL_DAY_BOTTOM_MARGIN = mResources.getDimension(R.dimen.all_day_bottom_margin) - HOURS_TEXT_SIZE = mResources.getDimension(R.dimen.hours_text_size) as Int.toFloat() - AMPM_TEXT_SIZE = mResources.getDimension(R.dimen.ampm_text_size) as Int.toFloat() - MIN_HOURS_WIDTH = mResources.getDimension(R.dimen.min_hours_width) - HOURS_LEFT_MARGIN = mResources.getDimension(R.dimen.hours_left_margin) - HOURS_RIGHT_MARGIN = mResources.getDimension(R.dimen.hours_right_margin) - MULTI_DAY_HEADER_HEIGHT = mResources.getDimension(R.dimen.day_header_height) + mResources.getDimension(R.dimen.date_header_text_size).toInt().toFloat() + DAY_HEADER_FONT_SIZE = + mResources.getDimension(R.dimen.day_label_text_size).toInt().toFloat() + ONE_DAY_HEADER_HEIGHT = mResources.getDimension(R.dimen.one_day_header_height).toInt() + DAY_HEADER_BOTTOM_MARGIN = mResources.getDimension(R.dimen.day_header_bottom_margin).toInt() + EXPAND_ALL_DAY_BOTTOM_MARGIN = + mResources.getDimension(R.dimen.all_day_bottom_margin).toInt() + HOURS_TEXT_SIZE = mResources.getDimension(R.dimen.hours_text_size).toInt().toFloat() + AMPM_TEXT_SIZE = mResources.getDimension(R.dimen.ampm_text_size).toInt().toFloat() + MIN_HOURS_WIDTH = mResources.getDimension(R.dimen.min_hours_width).toInt() + HOURS_LEFT_MARGIN = mResources.getDimension(R.dimen.hours_left_margin).toInt() + HOURS_RIGHT_MARGIN = mResources.getDimension(R.dimen.hours_right_margin).toInt() + MULTI_DAY_HEADER_HEIGHT = mResources.getDimension(R.dimen.day_header_height).toInt() val eventTextSizeId: Int eventTextSizeId = if (mNumDays == 1) { R.dimen.day_view_event_text_size } else { R.dimen.week_view_event_text_size } - EVENT_TEXT_FONT_SIZE = mResources.getDimension(eventTextSizeId) as Int.toFloat() - NEW_EVENT_HINT_FONT_SIZE = mResources.getDimension(R.dimen.new_event_hint_text_size) + EVENT_TEXT_FONT_SIZE = mResources.getDimension(eventTextSizeId).toFloat() + NEW_EVENT_HINT_FONT_SIZE = mResources.getDimension(R.dimen.new_event_hint_text_size).toInt() MIN_EVENT_HEIGHT = mResources.getDimension(R.dimen.event_min_height) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = MIN_EVENT_HEIGHT - EVENT_TEXT_TOP_MARGIN = mResources.getDimension(R.dimen.event_text_vertical_margin) + EVENT_TEXT_TOP_MARGIN = mResources.getDimension(R.dimen.event_text_vertical_margin).toInt() EVENT_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN EVENT_TEXT_LEFT_MARGIN = mResources - .getDimension(R.dimen.event_text_horizontal_margin) + .getDimension(R.dimen.event_text_horizontal_margin).toInt() EVENT_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN @@ -3867,14 +3961,14 @@ class DayView( mNewEventHintColor = mResources.getColor(R.color.new_event_hint_text_color) mAcceptedOrTentativeEventBoxDrawable = mResources .getDrawable(R.drawable.panel_month_event_holo_light) - mEventLoader = eventLoader + mEventLoader = eventLoader as EventLoader mEventGeometry = EventGeometry() mEventGeometry.setMinEventHeight(MIN_EVENT_HEIGHT) - mEventGeometry.setHourGap(HOUR_GAP) + mEventGeometry.setHourGap(HOUR_GAP.toFloat()) mEventGeometry.setCellMargin(DAY_GAP) mLastPopupEventID = INVALID_EVENT_ID - mController = controller - mViewSwitcher = viewSwitcher + mController = controller as CalendarController + mViewSwitcher = viewSwitcher as ViewSwitcher mGestureDetector = GestureDetector(context, CalendarGestureListener()) mScaleGestureDetector = ScaleGestureDetector(getContext(), this) if (mCellHeight == 0) { @@ -3891,6 +3985,6 @@ class DayView( mScaledPagingTouchSlop = vc.getScaledPagingTouchSlop() mOnDownDelay = ViewConfiguration.getTapTimeout() OVERFLING_DISTANCE = vc.getScaledOverflingDistance() - init(context) + init(context as Context) } } \ No newline at end of file -- cgit v1.2.3 From 2cba2a7819d117d7adabf9b77cf1c4d12b920bed Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 8 Jul 2021 15:18:49 +0000 Subject: AOSP/Calendar - Copy of EventInfoFragment.java The Java code in Event EventInfoFragment.java has been copied into a corresponding .kt file. Test: manual - opening both files shows that they are identical. Change-Id: I49c7240ba4ea471d7cc0fae53f402dfaf29b73c8 --- src/com/android/calendar/EventInfoFragment.kt | 877 ++++++++++++++++++++++++++ 1 file changed, 877 insertions(+) create mode 100644 src/com/android/calendar/EventInfoFragment.kt diff --git a/src/com/android/calendar/EventInfoFragment.kt b/src/com/android/calendar/EventInfoFragment.kt new file mode 100644 index 00000000..0aa83d02 --- /dev/null +++ b/src/com/android/calendar/EventInfoFragment.kt @@ -0,0 +1,877 @@ +/* + * Copyright (C) 2010 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 static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; +import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; +import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; +import static com.android.calendar.CalendarController.EVENT_EDIT_ON_LAUNCH; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.FragmentManager; +import android.app.Service; +import android.content.ActivityNotFoundException; +import android.content.ContentProviderOperation; +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.SharedPreferences; +import android.content.pm.ApplicationInfo; +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; +import android.os.Bundle; +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.ContactsContract; +import android.provider.ContactsContract.CommonDataKinds; +import android.provider.ContactsContract.Intents; +import android.provider.ContactsContract.QuickContact; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.text.format.Time; +import android.text.method.LinkMovementMethod; +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; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnTouchListener; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.RadioGroup.OnCheckedChangeListener; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; + +import com.android.calendar.CalendarController.EventInfo; +import com.android.calendar.CalendarController.EventType; +import com.android.calendar.alerts.QuickResponseActivity; +import com.android.calendarcommon2.DateException; +import com.android.calendarcommon2.Duration; +import com.android.calendarcommon2.EventRecurrence; +import com.android.colorpicker.HsvColorComparator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class EventInfoFragment extends DialogFragment implements OnCheckedChangeListener, + CalendarController.EventHandler, OnClickListener { + + public static final boolean DEBUG = false; + + public static final String TAG = "EventInfoFragment"; + + protected static final String BUNDLE_KEY_EVENT_ID = "key_event_id"; + protected static final String BUNDLE_KEY_START_MILLIS = "key_start_millis"; + protected static final String BUNDLE_KEY_END_MILLIS = "key_end_millis"; + protected static final String BUNDLE_KEY_IS_DIALOG = "key_fragment_is_dialog"; + protected static final String BUNDLE_KEY_DELETE_DIALOG_VISIBLE = "key_delete_dialog_visible"; + protected static final String BUNDLE_KEY_WINDOW_STYLE = "key_window_style"; + protected static final String BUNDLE_KEY_CALENDAR_COLOR = "key_calendar_color"; + protected static final String BUNDLE_KEY_CALENDAR_COLOR_INIT = "key_calendar_color_init"; + protected static final String BUNDLE_KEY_CURRENT_COLOR = "key_current_color"; + protected static final String BUNDLE_KEY_CURRENT_COLOR_KEY = "key_current_color_key"; + protected static final String BUNDLE_KEY_CURRENT_COLOR_INIT = "key_current_color_init"; + protected static final String BUNDLE_KEY_ORIGINAL_COLOR = "key_original_color"; + protected static final String BUNDLE_KEY_ORIGINAL_COLOR_INIT = "key_original_color_init"; + protected static final String BUNDLE_KEY_ATTENDEE_RESPONSE = "key_attendee_response"; + protected static final String BUNDLE_KEY_USER_SET_ATTENDEE_RESPONSE = + "key_user_set_attendee_response"; + protected static final String BUNDLE_KEY_TENTATIVE_USER_RESPONSE = + "key_tentative_user_response"; + protected static final String BUNDLE_KEY_RESPONSE_WHICH_EVENTS = "key_response_which_events"; + protected static final String BUNDLE_KEY_REMINDER_MINUTES = "key_reminder_minutes"; + protected static final String BUNDLE_KEY_REMINDER_METHODS = "key_reminder_methods"; + + + 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. + */ + static final int UPDATE_SINGLE = 0; + static final int UPDATE_ALL = 1; + + // Style of view + public static final int FULL_WINDOW_STYLE = 0; + public static final int DIALOG_WINDOW_STYLE = 1; + + private int mWindowStyle = DIALOG_WINDOW_STYLE; + + // Query tokens for QueryHandler + private static final int TOKEN_QUERY_EVENT = 1 << 0; + private static final int TOKEN_QUERY_CALENDARS = 1 << 1; + private static final int TOKEN_QUERY_ATTENDEES = 1 << 2; + 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_COLORS; + + private int mCurrentQuery = 0; + + private static final String[] EVENT_PROJECTION = new String[] { + Events._ID, // 0 do not remove; used in DeleteEventHelper + Events.TITLE, // 1 do not remove; used in DeleteEventHelper + Events.RRULE, // 2 do not remove; used in DeleteEventHelper + Events.ALL_DAY, // 3 do not remove; used in DeleteEventHelper + Events.CALENDAR_ID, // 4 do not remove; used in DeleteEventHelper + Events.DTSTART, // 5 do not remove; used in DeleteEventHelper + Events._SYNC_ID, // 6 do not remove; used in DeleteEventHelper + Events.EVENT_TIMEZONE, // 7 do not remove; used in DeleteEventHelper + Events.DESCRIPTION, // 8 + Events.EVENT_LOCATION, // 9 + Calendars.CALENDAR_ACCESS_LEVEL, // 10 + 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; + private static final int EVENT_INDEX_RRULE = 2; + private static final int EVENT_INDEX_ALL_DAY = 3; + private static final int EVENT_INDEX_CALENDAR_ID = 4; + private static final int EVENT_INDEX_DTSTART = 5; + private static final int EVENT_INDEX_SYNC_ID = 6; + private static final int EVENT_INDEX_EVENT_TIMEZONE = 7; + 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_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; + + static { + if (!Utils.isJellybeanOrLater()) { + EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_PACKAGE] = Events._ID; // nonessential value + EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_URI] = Events._ID; // nonessential value + } + } + + static final String[] CALENDARS_PROJECTION = new String[] { + Calendars._ID, // 0 + Calendars.CALENDAR_DISPLAY_NAME, // 1 + Calendars.OWNER_ACCOUNT, // 2 + Calendars.CAN_ORGANIZER_RESPOND, // 3 + 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 + "=?"; + + public static final int COLORS_INDEX_COLOR = 1; + public static final int COLORS_INDEX_COLOR_KEY = 2; + + private View mView; + + private Uri mUri; + private long mEventId; + private Cursor mEventCursor; + private Cursor mCalendarsCursor; + + private static float mScale = 0; // Used for supporting different screen densities + + private static int mCustomAppIconSize = 32; + + private long mStartMillis; + private long mEndMillis; + private boolean mAllDay; + + private boolean mOwnerCanRespond; + private String mSyncAccountName; + private String mCalendarOwnerAccount; + private boolean mIsBusyFreeCalendar; + + private int mOriginalAttendeeResponse; + private int mAttendeeResponseFromIntent = Attendees.ATTENDEE_STATUS_NONE; + private int mUserSetResponse = Attendees.ATTENDEE_STATUS_NONE; + private int mWhichEvents = -1; + // Used as the temporary response until the dialog is confirmed. It is also + // able to be used as a state marker for configuration changes. + private int mTentativeUserSetResponse = Attendees.ATTENDEE_STATUS_NONE; + private boolean mHasAlarm; + // Used to prevent saving changes in event if it is being deleted. + private boolean mEventDeletionStarted = false; + + private TextView mTitle; + private TextView mWhenDateTime; + private TextView mWhere; + private Menu mMenu = null; + private View mHeadlines; + private ScrollView mScrollView; + private View mLoadingMsgView; + private View mErrorMsgView; + private ObjectAnimator mAnimateAlpha; + private long mLoadingMsgStartTime; + + private SparseIntArray mDisplayColorKeyMap = new SparseIntArray(); + private int mOriginalColor = -1; + private boolean mOriginalColorInitialized = false; + private int mCalendarColor = -1; + private boolean mCalendarColorInitialized = false; + private int mCurrentColor = -1; + private boolean mCurrentColorInitialized = false; + private int mCurrentColorKey = -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 + private RadioGroup mResponseRadioGroup; + + ArrayList mToEmails = new ArrayList(); + ArrayList mCcEmails = new ArrayList(); + + + private final Runnable mTZUpdater = new Runnable() { + @Override + public void run() { + updateEvent(mView); + } + }; + + private final Runnable mLoadingMsgAlphaUpdater = new Runnable() { + @Override + public void run() { + // Since this is run after a delay, make sure to only show the message + // if the event's data is not shown yet. + if (!mAnimateAlpha.isRunning() && mScrollView.getAlpha() == 0) { + mLoadingMsgStartTime = System.currentTimeMillis(); + mLoadingMsgView.setAlpha(1); + } + } + }; + + private static int mDialogWidth = 500; + private static int mDialogHeight = 600; + private static int DIALOG_TOP_MARGIN = 8; + private boolean mIsDialog = false; + private boolean mIsPaused = true; + private boolean mDismissOnResume = false; + private int mX = -1; + private int mY = -1; + private int mMinTop; // Dialog cannot be above this location + private boolean mIsTabletConfig; + private Activity mActivity; + private Context mContext; + + private CalendarController mController; + + private void sendAccessibilityEventIfQueryDone(int token) { + mCurrentQuery |= token; + if (mCurrentQuery == TOKEN_QUERY_ALL) { + sendAccessibilityEvent(); + } + } + + public EventInfoFragment(Context context, Uri uri, long startMillis, long endMillis, + int attendeeResponse, boolean isDialog, int windowStyle) { + + Resources r = context.getResources(); + if (mScale == 0) { + mScale = context.getResources().getDisplayMetrics().density; + if (mScale != 1) { + mCustomAppIconSize *= mScale; + if (isDialog) { + DIALOG_TOP_MARGIN *= mScale; + } + } + } + if (isDialog) { + setDialogSize(r); + } + mIsDialog = isDialog; + + setStyle(DialogFragment.STYLE_NO_TITLE, 0); + mUri = uri; + mStartMillis = startMillis; + mEndMillis = endMillis; + mAttendeeResponseFromIntent = attendeeResponse; + mWindowStyle = windowStyle; + } + + // This is currently required by the fragment manager. + 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, + endMillis, attendeeResponse, isDialog, windowStyle); + mEventId = eventId; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + if (mIsDialog) { + applyDialogParams(); + } + + final Activity activity = getActivity(); + mContext = activity; + } + + private void applyDialogParams() { + Dialog dialog = getDialog(); + dialog.setCanceledOnTouchOutside(true); + + Window window = dialog.getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); + + WindowManager.LayoutParams a = window.getAttributes(); + a.dimAmount = .4f; + + a.width = mDialogWidth; + a.height = mDialogHeight; + + + // On tablets , do smart positioning of dialog + // On phones , use the whole screen + + if (mX != -1 || mY != -1) { + a.x = mX - mDialogWidth / 2; + a.y = mY - mDialogHeight / 2; + if (a.y < mMinTop) { + a.y = mMinTop + DIALOG_TOP_MARGIN; + } + a.gravity = Gravity.LEFT | Gravity.TOP; + } + window.setAttributes(a); + } + + public void setDialogParams(int x, int y, int minTop) { + mX = x; + mY = y; + mMinTop = minTop; + } + + // Implements OnCheckedChangeListener + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + } + + public void onNothingSelected(AdapterView parent) { + } + + @Override + public void onDetach() { + super.onDetach(); + mController.deregisterEventHandler(R.layout.event_info); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mActivity = activity; + // Ensure that mIsTabletConfig is set before creating the menu. + mIsTabletConfig = Utils.getConfigBool(mActivity, R.bool.tablet_config); + mController = CalendarController.getInstance(mActivity); + mController.registerEventHandler(R.layout.event_info, this); + + if (!mIsDialog) { + setHasOptionsMenu(true); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + if (mWindowStyle == DIALOG_WINDOW_STYLE) { + mView = inflater.inflate(R.layout.event_info_dialog, container, false); + } else { + mView = inflater.inflate(R.layout.event_info, container, false); + } + mScrollView = (ScrollView) mView.findViewById(R.id.event_info_scroll_view); + mLoadingMsgView = mView.findViewById(R.id.event_info_loading_msg); + mErrorMsgView = mView.findViewById(R.id.event_info_error_msg); + mTitle = (TextView) mView.findViewById(R.id.title); + mWhenDateTime = (TextView) mView.findViewById(R.id.when_datetime); + mWhere = (TextView) mView.findViewById(R.id.where); + mHeadlines = mView.findViewById(R.id.event_info_headline); + + mResponseRadioGroup = (RadioGroup) mView.findViewById(R.id.response_value); + + mAnimateAlpha = ObjectAnimator.ofFloat(mScrollView, "Alpha", 0, 1); + mAnimateAlpha.setDuration(FADE_IN_TIME); + mAnimateAlpha.addListener(new AnimatorListenerAdapter() { + int defLayerType; + + @Override + public void onAnimationStart(Animator animation) { + // Use hardware layer for better performance during animation + defLayerType = mScrollView.getLayerType(); + mScrollView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + // Ensure that the loading message is gone before showing the + // event info + mLoadingMsgView.removeCallbacks(mLoadingMsgAlphaUpdater); + mLoadingMsgView.setVisibility(View.GONE); + } + + @Override + public void onAnimationCancel(Animator animation) { + mScrollView.setLayerType(defLayerType, null); + } + + @Override + public void onAnimationEnd(Animator animation) { + mScrollView.setLayerType(defLayerType, null); + // Do not cross fade after the first time + mNoCrossFade = true; + } + }); + + mLoadingMsgView.setAlpha(0); + mScrollView.setAlpha(0); + mErrorMsgView.setVisibility(View.INVISIBLE); + mLoadingMsgView.postDelayed(mLoadingMsgAlphaUpdater, LOADING_MSG_DELAY); + + // 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); + } + + return mView; + } + + private void updateTitle() { + Resources res = getActivity().getResources(); + getActivity().setTitle(res.getString(R.string.event_info_title)); + } + + /** + * Initializes the event cursor, which is expected to point to the first + * (and only) result from a query. + * @return false if the cursor is empty, true otherwise + */ + private boolean initEventCursor() { + if ((mEventCursor == null) || (mEventCursor.getCount() == 0)) { + return false; + } + mEventCursor.moveToFirst(); + mEventId = mEventCursor.getInt(EVENT_INDEX_ID); + String rRule = mEventCursor.getString(EVENT_INDEX_RRULE); + // mHasAlarm will be true if it was saved in the event already. + mHasAlarm = (mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) == 1)? true : false; + return true; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + // Show color/edit/delete buttons only in non-dialog configuration + if (!mIsDialog && !mIsTabletConfig || mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) { + inflater.inflate(R.menu.event_info_title_bar, menu); + mMenu = menu; + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + // If we're a dialog we don't want to handle menu buttons + if (mIsDialog) { + return false; + } + // 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 + + final int itemId = item.getItemId(); + if (itemId == android.R.id.home) { + Utils.returnToCalendarHome(mContext); + mActivity.finish(); + return true; + } else if (itemId == R.id.info_action_edit) { + mActivity.finish(); + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onStop() { + super.onStop(); + } + + @Override + public void onDestroy() { + if (mEventCursor != null) { + mEventCursor.close(); + } + if (mCalendarsCursor != null) { + mCalendarsCursor.close(); + } + super.onDestroy(); + } + + /** + * Creates an exception to a recurring event. The only change we're making is to the + * "self attendee status" value. The provider will take care of updating the corresponding + * Attendees.attendeeStatus entry. + * + * @param eventId The recurring event. + * @param status The new value for selfAttendeeStatus. + */ + private void createExceptionResponse(long eventId, int status) { + ContentValues values = new ContentValues(); + values.put(Events.ORIGINAL_INSTANCE_TIME, mStartMillis); + values.put(Events.SELF_ATTENDEE_STATUS, status); + values.put(Events.STATUS, Events.STATUS_CONFIRMED); + + ArrayList ops = new ArrayList(); + Uri exceptionUri = Uri.withAppendedPath(Events.CONTENT_EXCEPTION_URI, + String.valueOf(eventId)); + ops.add(ContentProviderOperation.newInsert(exceptionUri).withValues(values).build()); + } + + public static int getResponseFromButtonId(int buttonId) { + return Attendees.ATTENDEE_STATUS_NONE; + } + + public static int findButtonIdForResponse(int response) { + return -1; + } + + private void displayEventNotFound() { + mErrorMsgView.setVisibility(View.VISIBLE); + mScrollView.setVisibility(View.GONE); + mLoadingMsgView.setVisibility(View.GONE); + } + + private void updateEvent(View view) { + if (mEventCursor == null || view == null) { + return; + } + + Context context = view.getContext(); + if (context == null) { + return; + } + + String eventName = mEventCursor.getString(EVENT_INDEX_TITLE); + if (eventName == null || eventName.length() == 0) { + eventName = getActivity().getString(R.string.no_title_label); + } + + // 3rd parties might not have specified the start/end time when firing the + // Events.CONTENT_URI intent. Update these with values read from the db. + if (mStartMillis == 0 && mEndMillis == 0) { + mStartMillis = mEventCursor.getLong(EVENT_INDEX_DTSTART); + mEndMillis = mEventCursor.getLong(EVENT_INDEX_DTEND); + if (mEndMillis == 0) { + String duration = mEventCursor.getString(EVENT_INDEX_DURATION); + if (!TextUtils.isEmpty(duration)) { + try { + Duration d = new Duration(); + d.parse(duration); + long endMillis = mStartMillis + d.getMillis(); + if (endMillis >= mStartMillis) { + mEndMillis = endMillis; + } else { + Log.d(TAG, "Invalid duration string: " + duration); + } + } catch (DateException e) { + Log.d(TAG, "Error parsing duration string " + duration, e); + } + } + if (mEndMillis == 0) { + mEndMillis = mStartMillis; + } + } + } + + mAllDay = mEventCursor.getInt(EVENT_INDEX_ALL_DAY) != 0; + String location = mEventCursor.getString(EVENT_INDEX_EVENT_LOCATION); + String description = mEventCursor.getString(EVENT_INDEX_DESCRIPTION); + String rRule = mEventCursor.getString(EVENT_INDEX_RRULE); + String eventTimezone = mEventCursor.getString(EVENT_INDEX_EVENT_TIMEZONE); + + mHeadlines.setBackgroundColor(mCurrentColor); + + // What + if (eventName != null) { + setTextCommon(view, R.id.title, eventName); + } + + // When + // Set the date and repeats (if any) + String localTimezone = Utils.getTimeZone(mActivity, mTZUpdater); + + Resources resources = context.getResources(); + String displayedDatetime = Utils.getDisplayedDatetime(mStartMillis, mEndMillis, + System.currentTimeMillis(), localTimezone, mAllDay, context); + + String displayedTimezone = null; + if (!mAllDay) { + displayedTimezone = Utils.getDisplayedTimezone(mStartMillis, localTimezone, + eventTimezone); + } + // Display the datetime. Make the timezone (if any) transparent. + if (displayedTimezone == null) { + setTextCommon(view, R.id.when_datetime, displayedDatetime); + } else { + int timezoneIndex = displayedDatetime.length(); + displayedDatetime += " " + displayedTimezone; + SpannableStringBuilder sb = new SpannableStringBuilder(displayedDatetime); + ForegroundColorSpan transparentColorSpan = new ForegroundColorSpan( + resources.getColor(R.color.event_info_headline_transparent_color)); + sb.setSpan(transparentColorSpan, timezoneIndex, displayedDatetime.length(), + Spannable.SPAN_INCLUSIVE_INCLUSIVE); + setTextCommon(view, R.id.when_datetime, sb); + } + + view.findViewById(R.id.when_repeat).setVisibility(View.GONE); + + // Organizer view is setup in the updateCalendar method + + + // Where + if (location == null || location.trim().length() == 0) { + setVisibilityCommon(view, R.id.where, View.GONE); + } else { + final TextView textView = mWhere; + if (textView != null) { + textView.setText(location.trim()); + } + } + + // Launch Custom App + if (Utils.isJellybeanOrLater()) { + updateCustomAppButton(); + } + } + + private void updateCustomAppButton() { + setVisibilityCommon(mView, R.id.launch_custom_app_container, View.GONE); + return; + } + + private void sendAccessibilityEvent() { + AccessibilityManager am = + (AccessibilityManager) getActivity().getSystemService(Service.ACCESSIBILITY_SERVICE); + if (!am.isEnabled()) { + return; + } + + AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED); + event.setClassName(EventInfoFragment.class.getName()); + event.setPackageName(getActivity().getPackageName()); + List text = event.getText(); + + if (mResponseRadioGroup.getVisibility() == View.VISIBLE) { + int id = mResponseRadioGroup.getCheckedRadioButtonId(); + if (id != View.NO_ID) { + text.add(((TextView) getView().findViewById(R.id.response_label)).getText()); + text.add((((RadioButton) (mResponseRadioGroup.findViewById(id))) + .getText() + PERIOD_SPACE)); + } + } + + am.sendAccessibilityEvent(event); + } + + private void updateCalendar(View view) { + + mCalendarOwnerAccount = ""; + if (mCalendarsCursor != null && mEventCursor != null) { + mCalendarsCursor.moveToFirst(); + String tempAccount = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT); + mCalendarOwnerAccount = (tempAccount == null) ? "" : tempAccount; + mOwnerCanRespond = mCalendarsCursor.getInt(CALENDARS_INDEX_OWNER_CAN_RESPOND) != 0; + mSyncAccountName = mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_NAME); + + setVisibilityCommon(view, R.id.organizer_container, View.GONE); + mIsBusyFreeCalendar = + mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) == Calendars.CAL_ACCESS_FREEBUSY; + + if (!mIsBusyFreeCalendar) { + + View b = mView.findViewById(R.id.edit); + b.setEnabled(true); + b.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + // For dialogs, just close the fragment + // For full screen, close activity on phone, leave it for tablet + if (mIsDialog) { + EventInfoFragment.this.dismiss(); + } + else if (!mIsTabletConfig){ + getActivity().finish(); + } + } + }); + } + View button; + if ((!mIsDialog && !mIsTabletConfig || + mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) && mMenu != null) { + mActivity.invalidateOptionsMenu(); + } + } else { + setVisibilityCommon(view, R.id.calendar, View.GONE); + sendAccessibilityEventIfQueryDone(TOKEN_QUERY_DUPLICATE_CALENDARS); + } + } + + private void setTextCommon(View view, int id, CharSequence text) { + TextView textView = (TextView) view.findViewById(id); + if (textView == null) + return; + textView.setText(text); + } + + private void setVisibilityCommon(View view, int id, int visibility) { + View v = view.findViewById(id); + if (v != null) { + v.setVisibility(visibility); + } + return; + } + + @Override + public void onPause() { + mIsPaused = true; + super.onPause(); + } + + @Override + public void onResume() { + super.onResume(); + if (mIsDialog) { + setDialogSize(getActivity().getResources()); + applyDialogParams(); + } + mIsPaused = false; + if (mTentativeUserSetResponse != Attendees.ATTENDEE_STATUS_NONE) { + int buttonId = findButtonIdForResponse(mTentativeUserSetResponse); + mResponseRadioGroup.check(buttonId); + } + } + + @Override + public void eventsChanged() { + } + + @Override + public long getSupportedEventTypes() { + return EventType.EVENTS_CHANGED; + } + + @Override + public void handleEvent(EventInfo event) { + reloadEvents(); + } + + public void reloadEvents() { + } + + @Override + public void onClick(View view) { + } + + public long getEventId() { + return mEventId; + } + + public long getStartMillis() { + return mStartMillis; + } + public long getEndMillis() { + return mEndMillis; + } + private void setDialogSize(Resources r) { + mDialogWidth = (int)r.getDimension(R.dimen.event_info_dialog_width); + mDialogHeight = (int)r.getDimension(R.dimen.event_info_dialog_height); + } +} -- cgit v1.2.3 From 44caaf005bc49acded904a1b2923510287a68598 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 8 Jul 2021 15:25:22 +0000 Subject: AOSP/Calendar - Initial Conversion of EventInfoFragment This is how Android Studio's built-in converter converted EventInfoFragment.java to Kotlin without any additional edits. Change-Id: I047a566b7784475933c7db94823ce7affaabc25e --- src/com/android/calendar/EventInfoFragment.kt | 1220 ++++++++++++------------- 1 file changed, 580 insertions(+), 640 deletions(-) diff --git a/src/com/android/calendar/EventInfoFragment.kt b/src/com/android/calendar/EventInfoFragment.kt index 0aa83d02..816bff48 100644 --- a/src/com/android/calendar/EventInfoFragment.kt +++ b/src/com/android/calendar/EventInfoFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,512 +13,483 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar + +import android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY +import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME +import android.provider.CalendarContract.EXTRA_EVENT_END_TIME +import com.android.calendar.CalendarController.EVENT_EDIT_ON_LAUNCH +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ObjectAnimator +import android.app.Activity +import android.app.Dialog +import android.app.DialogFragment +import android.app.FragmentManager +import android.app.Service +import android.content.ActivityNotFoundException +import android.content.ContentProviderOperation +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.SharedPreferences +import android.content.pm.ApplicationInfo +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 +import android.os.Bundle +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.ContactsContract +import android.provider.ContactsContract.CommonDataKinds +import android.provider.ContactsContract.Intents +import android.provider.ContactsContract.QuickContact +import android.text.Spannable +import android.text.SpannableStringBuilder +import android.text.TextUtils +import android.text.format.Time +import android.text.method.LinkMovementMethod +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 +import android.view.MenuInflater +import android.view.MenuItem +import android.view.MotionEvent +import android.view.View +import android.view.View.OnClickListener +import android.view.View.OnTouchListener +import android.view.ViewGroup +import android.view.Window +import android.view.WindowManager +import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityManager +import android.widget.AdapterView +import android.widget.AdapterView.OnItemSelectedListener +import android.widget.Button +import android.widget.LinearLayout +import android.widget.RadioButton +import android.widget.RadioGroup +import android.widget.RadioGroup.OnCheckedChangeListener +import android.widget.ScrollView +import android.widget.TextView +import android.widget.Toast +import com.android.calendar.CalendarController.EventInfo +import com.android.calendar.CalendarController.EventType +import com.android.calendar.alerts.QuickResponseActivity +import com.android.calendarcommon2.DateException +import com.android.calendarcommon2.Duration +import com.android.calendarcommon2.EventRecurrence +import com.android.colorpicker.HsvColorComparator +import java.util.ArrayList +import java.util.Arrays +import java.util.Collections +import java.util.List + +class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarController.EventHandler, + OnClickListener { + private var mWindowStyle = DIALOG_WINDOW_STYLE + private var mCurrentQuery = 0 + + companion object { + const val DEBUG = false + const val TAG = "EventInfoFragment" + protected const val BUNDLE_KEY_EVENT_ID = "key_event_id" + protected const val BUNDLE_KEY_START_MILLIS = "key_start_millis" + protected const val BUNDLE_KEY_END_MILLIS = "key_end_millis" + protected const val BUNDLE_KEY_IS_DIALOG = "key_fragment_is_dialog" + protected const val BUNDLE_KEY_DELETE_DIALOG_VISIBLE = "key_delete_dialog_visible" + protected const val BUNDLE_KEY_WINDOW_STYLE = "key_window_style" + protected const val BUNDLE_KEY_CALENDAR_COLOR = "key_calendar_color" + protected const val BUNDLE_KEY_CALENDAR_COLOR_INIT = "key_calendar_color_init" + protected const val BUNDLE_KEY_CURRENT_COLOR = "key_current_color" + protected const val BUNDLE_KEY_CURRENT_COLOR_KEY = "key_current_color_key" + protected const val BUNDLE_KEY_CURRENT_COLOR_INIT = "key_current_color_init" + protected const val BUNDLE_KEY_ORIGINAL_COLOR = "key_original_color" + protected const val BUNDLE_KEY_ORIGINAL_COLOR_INIT = "key_original_color_init" + protected const val BUNDLE_KEY_ATTENDEE_RESPONSE = "key_attendee_response" + protected const val BUNDLE_KEY_USER_SET_ATTENDEE_RESPONSE = "key_user_set_attendee_response" + protected const val BUNDLE_KEY_TENTATIVE_USER_RESPONSE = "key_tentative_user_response" + protected const val BUNDLE_KEY_RESPONSE_WHICH_EVENTS = "key_response_which_events" + protected const val BUNDLE_KEY_REMINDER_MINUTES = "key_reminder_minutes" + protected const val BUNDLE_KEY_REMINDER_METHODS = "key_reminder_methods" + private const val PERIOD_SPACE = ". " + private const val NO_EVENT_COLOR = "" + + /** + * These are the corresponding indices into the array of strings + * "R.array.change_response_labels" in the resource file. + */ + const val UPDATE_SINGLE = 0 + const val UPDATE_ALL = 1 + + // Style of view + const val FULL_WINDOW_STYLE = 0 + const val DIALOG_WINDOW_STYLE = 1 + + // Query tokens for QueryHandler + private const val TOKEN_QUERY_EVENT = 1 shl 0 + private const val TOKEN_QUERY_CALENDARS = 1 shl 1 + private const val TOKEN_QUERY_ATTENDEES = 1 shl 2 + private const val TOKEN_QUERY_DUPLICATE_CALENDARS = 1 shl 3 + private const val TOKEN_QUERY_REMINDERS = 1 shl 4 + private const val TOKEN_QUERY_VISIBLE_CALENDARS = 1 shl 5 + private const val TOKEN_QUERY_COLORS = 1 shl 6 + private const val TOKEN_QUERY_ALL = (TOKEN_QUERY_DUPLICATE_CALENDARS + or TOKEN_QUERY_ATTENDEES or TOKEN_QUERY_CALENDARS or TOKEN_QUERY_EVENT + or TOKEN_QUERY_REMINDERS or TOKEN_QUERY_VISIBLE_CALENDARS or TOKEN_QUERY_COLORS) + private val EVENT_PROJECTION = arrayOf( + Events._ID, // 0 do not remove; used in DeleteEventHelper + Events.TITLE, // 1 do not remove; used in DeleteEventHelper + Events.RRULE, // 2 do not remove; used in DeleteEventHelper + Events.ALL_DAY, // 3 do not remove; used in DeleteEventHelper + Events.CALENDAR_ID, // 4 do not remove; used in DeleteEventHelper + Events.DTSTART, // 5 do not remove; used in DeleteEventHelper + Events._SYNC_ID, // 6 do not remove; used in DeleteEventHelper + Events.EVENT_TIMEZONE, // 7 do not remove; used in DeleteEventHelper + Events.DESCRIPTION, // 8 + Events.EVENT_LOCATION, // 9 + Calendars.CALENDAR_ACCESS_LEVEL, // 10 + 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 const val EVENT_INDEX_ID = 0 + private const val EVENT_INDEX_TITLE = 1 + private const val EVENT_INDEX_RRULE = 2 + private const val EVENT_INDEX_ALL_DAY = 3 + private const val EVENT_INDEX_CALENDAR_ID = 4 + private const val EVENT_INDEX_DTSTART = 5 + private const val EVENT_INDEX_SYNC_ID = 6 + private const val EVENT_INDEX_EVENT_TIMEZONE = 7 + private const val EVENT_INDEX_DESCRIPTION = 8 + private const val EVENT_INDEX_EVENT_LOCATION = 9 + private const val EVENT_INDEX_ACCESS_LEVEL = 10 + private const val EVENT_INDEX_CALENDAR_COLOR = 11 + private const val EVENT_INDEX_EVENT_COLOR = 12 + private const val EVENT_INDEX_HAS_ATTENDEE_DATA = 13 + private const val EVENT_INDEX_ORGANIZER = 14 + private const val EVENT_INDEX_HAS_ALARM = 15 + private const val EVENT_INDEX_MAX_REMINDERS = 16 + private const val EVENT_INDEX_ALLOWED_REMINDERS = 17 + private const val EVENT_INDEX_CUSTOM_APP_PACKAGE = 18 + private const val EVENT_INDEX_CUSTOM_APP_URI = 19 + private const val EVENT_INDEX_DTEND = 20 + private const val EVENT_INDEX_DURATION = 21 + val CALENDARS_PROJECTION = arrayOf( + Calendars._ID, // 0 + Calendars.CALENDAR_DISPLAY_NAME, // 1 + Calendars.OWNER_ACCOUNT, // 2 + Calendars.CAN_ORGANIZER_RESPOND, // 3 + Calendars.ACCOUNT_NAME, // 4 + Calendars.ACCOUNT_TYPE // 5 + ) + const val CALENDARS_INDEX_DISPLAY_NAME = 1 + const val CALENDARS_INDEX_OWNER_ACCOUNT = 2 + const val CALENDARS_INDEX_OWNER_CAN_RESPOND = 3 + const val CALENDARS_INDEX_ACCOUNT_NAME = 4 + const val CALENDARS_INDEX_ACCOUNT_TYPE = 5 + val CALENDARS_WHERE: String = Calendars._ID.toString() + "=?" + val CALENDARS_DUPLICATE_NAME_WHERE: String = + Calendars.CALENDAR_DISPLAY_NAME.toString() + "=?" + val CALENDARS_VISIBLE_WHERE: String = Calendars.VISIBLE.toString() + "=?" + const val COLORS_INDEX_COLOR = 1 + const val COLORS_INDEX_COLOR_KEY = 2 + private var mScale = 0f // Used for supporting different screen densities + private var mCustomAppIconSize = 32 + private const val FADE_IN_TIME = 300 // in milliseconds + private const val LOADING_MSG_DELAY = 600 // in milliseconds + private const val LOADING_MSG_MIN_DISPLAY_TIME = 600 + private var mDialogWidth = 500 + private var mDialogHeight = 600 + private var DIALOG_TOP_MARGIN = 8 + fun getResponseFromButtonId(buttonId: Int): Int { + return Attendees.ATTENDEE_STATUS_NONE + } + + fun findButtonIdForResponse(response: Int): Int { + return -1 + } + + init { + if (!Utils.isJellybeanOrLater()) { + EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_PACKAGE] = Events._ID // nonessential value + EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_URI] = Events._ID // nonessential value + } + } + } -package com.android.calendar; - -import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; -import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; -import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; -import static com.android.calendar.CalendarController.EVENT_EDIT_ON_LAUNCH; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.app.Activity; -import android.app.Dialog; -import android.app.DialogFragment; -import android.app.FragmentManager; -import android.app.Service; -import android.content.ActivityNotFoundException; -import android.content.ContentProviderOperation; -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.SharedPreferences; -import android.content.pm.ApplicationInfo; -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; -import android.os.Bundle; -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.ContactsContract; -import android.provider.ContactsContract.CommonDataKinds; -import android.provider.ContactsContract.Intents; -import android.provider.ContactsContract.QuickContact; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.text.format.Time; -import android.text.method.LinkMovementMethod; -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; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnTouchListener; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.RadioButton; -import android.widget.RadioGroup; -import android.widget.RadioGroup.OnCheckedChangeListener; -import android.widget.ScrollView; -import android.widget.TextView; -import android.widget.Toast; - -import com.android.calendar.CalendarController.EventInfo; -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.alerts.QuickResponseActivity; -import com.android.calendarcommon2.DateException; -import com.android.calendarcommon2.Duration; -import com.android.calendarcommon2.EventRecurrence; -import com.android.colorpicker.HsvColorComparator; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -public class EventInfoFragment extends DialogFragment implements OnCheckedChangeListener, - CalendarController.EventHandler, OnClickListener { - - public static final boolean DEBUG = false; - - public static final String TAG = "EventInfoFragment"; - - protected static final String BUNDLE_KEY_EVENT_ID = "key_event_id"; - protected static final String BUNDLE_KEY_START_MILLIS = "key_start_millis"; - protected static final String BUNDLE_KEY_END_MILLIS = "key_end_millis"; - protected static final String BUNDLE_KEY_IS_DIALOG = "key_fragment_is_dialog"; - protected static final String BUNDLE_KEY_DELETE_DIALOG_VISIBLE = "key_delete_dialog_visible"; - protected static final String BUNDLE_KEY_WINDOW_STYLE = "key_window_style"; - protected static final String BUNDLE_KEY_CALENDAR_COLOR = "key_calendar_color"; - protected static final String BUNDLE_KEY_CALENDAR_COLOR_INIT = "key_calendar_color_init"; - protected static final String BUNDLE_KEY_CURRENT_COLOR = "key_current_color"; - protected static final String BUNDLE_KEY_CURRENT_COLOR_KEY = "key_current_color_key"; - protected static final String BUNDLE_KEY_CURRENT_COLOR_INIT = "key_current_color_init"; - protected static final String BUNDLE_KEY_ORIGINAL_COLOR = "key_original_color"; - protected static final String BUNDLE_KEY_ORIGINAL_COLOR_INIT = "key_original_color_init"; - protected static final String BUNDLE_KEY_ATTENDEE_RESPONSE = "key_attendee_response"; - protected static final String BUNDLE_KEY_USER_SET_ATTENDEE_RESPONSE = - "key_user_set_attendee_response"; - protected static final String BUNDLE_KEY_TENTATIVE_USER_RESPONSE = - "key_tentative_user_response"; - protected static final String BUNDLE_KEY_RESPONSE_WHICH_EVENTS = "key_response_which_events"; - protected static final String BUNDLE_KEY_REMINDER_MINUTES = "key_reminder_minutes"; - protected static final String BUNDLE_KEY_REMINDER_METHODS = "key_reminder_methods"; - - - private static final String PERIOD_SPACE = ". "; - - private static final String NO_EVENT_COLOR = ""; + private var mView: View? = null + private var mUri: Uri? = null + var eventId: Long = 0 + private set + private val mEventCursor: Cursor? = null + private val mCalendarsCursor: Cursor? = null + var startMillis: Long = 0 + private set + var endMillis: Long = 0 + private set + private var mAllDay = false + private var mOwnerCanRespond = false + private var mSyncAccountName: String? = null + private var mCalendarOwnerAccount: String? = null + private var mIsBusyFreeCalendar = false + private val mOriginalAttendeeResponse = 0 + private var mAttendeeResponseFromIntent: Int = Attendees.ATTENDEE_STATUS_NONE + private val mUserSetResponse: Int = Attendees.ATTENDEE_STATUS_NONE + private val mWhichEvents = -1 - /** - * These are the corresponding indices into the array of strings - * "R.array.change_response_labels" in the resource file. - */ - static final int UPDATE_SINGLE = 0; - static final int UPDATE_ALL = 1; - - // Style of view - public static final int FULL_WINDOW_STYLE = 0; - public static final int DIALOG_WINDOW_STYLE = 1; - - private int mWindowStyle = DIALOG_WINDOW_STYLE; - - // Query tokens for QueryHandler - private static final int TOKEN_QUERY_EVENT = 1 << 0; - private static final int TOKEN_QUERY_CALENDARS = 1 << 1; - private static final int TOKEN_QUERY_ATTENDEES = 1 << 2; - 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_COLORS; - - private int mCurrentQuery = 0; - - private static final String[] EVENT_PROJECTION = new String[] { - Events._ID, // 0 do not remove; used in DeleteEventHelper - Events.TITLE, // 1 do not remove; used in DeleteEventHelper - Events.RRULE, // 2 do not remove; used in DeleteEventHelper - Events.ALL_DAY, // 3 do not remove; used in DeleteEventHelper - Events.CALENDAR_ID, // 4 do not remove; used in DeleteEventHelper - Events.DTSTART, // 5 do not remove; used in DeleteEventHelper - Events._SYNC_ID, // 6 do not remove; used in DeleteEventHelper - Events.EVENT_TIMEZONE, // 7 do not remove; used in DeleteEventHelper - Events.DESCRIPTION, // 8 - Events.EVENT_LOCATION, // 9 - Calendars.CALENDAR_ACCESS_LEVEL, // 10 - 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; - private static final int EVENT_INDEX_RRULE = 2; - private static final int EVENT_INDEX_ALL_DAY = 3; - private static final int EVENT_INDEX_CALENDAR_ID = 4; - private static final int EVENT_INDEX_DTSTART = 5; - private static final int EVENT_INDEX_SYNC_ID = 6; - private static final int EVENT_INDEX_EVENT_TIMEZONE = 7; - 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_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; - - static { - if (!Utils.isJellybeanOrLater()) { - EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_PACKAGE] = Events._ID; // nonessential value - EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_URI] = Events._ID; // nonessential value - } - } - - static final String[] CALENDARS_PROJECTION = new String[] { - Calendars._ID, // 0 - Calendars.CALENDAR_DISPLAY_NAME, // 1 - Calendars.OWNER_ACCOUNT, // 2 - Calendars.CAN_ORGANIZER_RESPOND, // 3 - 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 + "=?"; - - public static final int COLORS_INDEX_COLOR = 1; - public static final int COLORS_INDEX_COLOR_KEY = 2; - - private View mView; - - private Uri mUri; - private long mEventId; - private Cursor mEventCursor; - private Cursor mCalendarsCursor; - - private static float mScale = 0; // Used for supporting different screen densities - - private static int mCustomAppIconSize = 32; - - private long mStartMillis; - private long mEndMillis; - private boolean mAllDay; - - private boolean mOwnerCanRespond; - private String mSyncAccountName; - private String mCalendarOwnerAccount; - private boolean mIsBusyFreeCalendar; - - private int mOriginalAttendeeResponse; - private int mAttendeeResponseFromIntent = Attendees.ATTENDEE_STATUS_NONE; - private int mUserSetResponse = Attendees.ATTENDEE_STATUS_NONE; - private int mWhichEvents = -1; // Used as the temporary response until the dialog is confirmed. It is also // able to be used as a state marker for configuration changes. - private int mTentativeUserSetResponse = Attendees.ATTENDEE_STATUS_NONE; - private boolean mHasAlarm; + private val mTentativeUserSetResponse: Int = Attendees.ATTENDEE_STATUS_NONE + private var mHasAlarm = false + // Used to prevent saving changes in event if it is being deleted. - private boolean mEventDeletionStarted = false; - - private TextView mTitle; - private TextView mWhenDateTime; - private TextView mWhere; - private Menu mMenu = null; - private View mHeadlines; - private ScrollView mScrollView; - private View mLoadingMsgView; - private View mErrorMsgView; - private ObjectAnimator mAnimateAlpha; - private long mLoadingMsgStartTime; - - private SparseIntArray mDisplayColorKeyMap = new SparseIntArray(); - private int mOriginalColor = -1; - private boolean mOriginalColorInitialized = false; - private int mCalendarColor = -1; - private boolean mCalendarColorInitialized = false; - private int mCurrentColor = -1; - private boolean mCurrentColorInitialized = false; - private int mCurrentColorKey = -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 - private RadioGroup mResponseRadioGroup; - - ArrayList mToEmails = new ArrayList(); - ArrayList mCcEmails = new ArrayList(); - - - private final Runnable mTZUpdater = new Runnable() { + private val mEventDeletionStarted = false + private var mTitle: TextView? = null + private var mWhenDateTime: TextView? = null + private var mWhere: TextView? = null + private var mMenu: Menu? = null + private var mHeadlines: View? = null + private var mScrollView: ScrollView? = null + private var mLoadingMsgView: View? = null + private var mErrorMsgView: View? = null + private var mAnimateAlpha: ObjectAnimator? = null + private var mLoadingMsgStartTime: Long = 0 + private val mDisplayColorKeyMap: SparseIntArray = SparseIntArray() + private val mOriginalColor = -1 + private val mOriginalColorInitialized = false + private val mCalendarColor = -1 + private val mCalendarColorInitialized = false + private val mCurrentColor = -1 + private val mCurrentColorInitialized = false + private val mCurrentColorKey = -1 + private var mNoCrossFade = false // Used to prevent repeated cross-fade + private var mResponseRadioGroup: RadioGroup? = null + var mToEmails: ArrayList = ArrayList() + var mCcEmails: ArrayList = ArrayList() + private val mTZUpdater: Runnable = object : Runnable() { @Override - public void run() { - updateEvent(mView); + fun run() { + updateEvent(mView) } - }; - - private final Runnable mLoadingMsgAlphaUpdater = new Runnable() { + } + private val mLoadingMsgAlphaUpdater: Runnable = object : Runnable() { @Override - public void run() { + fun run() { // Since this is run after a delay, make sure to only show the message // if the event's data is not shown yet. - if (!mAnimateAlpha.isRunning() && mScrollView.getAlpha() == 0) { - mLoadingMsgStartTime = System.currentTimeMillis(); - mLoadingMsgView.setAlpha(1); + if (!mAnimateAlpha.isRunning() && mScrollView.getAlpha() === 0) { + mLoadingMsgStartTime = System.currentTimeMillis() + mLoadingMsgView.setAlpha(1) } } - }; - - private static int mDialogWidth = 500; - private static int mDialogHeight = 600; - private static int DIALOG_TOP_MARGIN = 8; - private boolean mIsDialog = false; - private boolean mIsPaused = true; - private boolean mDismissOnResume = false; - private int mX = -1; - private int mY = -1; - private int mMinTop; // Dialog cannot be above this location - private boolean mIsTabletConfig; - private Activity mActivity; - private Context mContext; - - private CalendarController mController; - - private void sendAccessibilityEventIfQueryDone(int token) { - mCurrentQuery |= token; + } + private var mIsDialog = false + private var mIsPaused = true + private val mDismissOnResume = false + private var mX = -1 + private var mY = -1 + private var mMinTop // Dialog cannot be above this location + = 0 + private var mIsTabletConfig = false + private var mActivity: Activity? = null + private var mContext: Context? = null + private var mController: CalendarController? = null + private fun sendAccessibilityEventIfQueryDone(token: Int) { + mCurrentQuery = mCurrentQuery or token if (mCurrentQuery == TOKEN_QUERY_ALL) { - sendAccessibilityEvent(); + sendAccessibilityEvent() } } - public EventInfoFragment(Context context, Uri uri, long startMillis, long endMillis, - int attendeeResponse, boolean isDialog, int windowStyle) { - - Resources r = context.getResources(); - if (mScale == 0) { - mScale = context.getResources().getDisplayMetrics().density; - if (mScale != 1) { - mCustomAppIconSize *= mScale; + constructor( + context: Context, uri: Uri?, startMillis: Long, endMillis: Long, + attendeeResponse: Int, isDialog: Boolean, windowStyle: Int + ) { + val r: Resources = context.getResources() + if (mScale == 0f) { + mScale = context.getResources().getDisplayMetrics().density + if (mScale != 1f) { + mCustomAppIconSize *= mScale.toInt() if (isDialog) { - DIALOG_TOP_MARGIN *= mScale; + DIALOG_TOP_MARGIN *= mScale.toInt() } } } if (isDialog) { - setDialogSize(r); + setDialogSize(r) } - mIsDialog = isDialog; - - setStyle(DialogFragment.STYLE_NO_TITLE, 0); - mUri = uri; - mStartMillis = startMillis; - mEndMillis = endMillis; - mAttendeeResponseFromIntent = attendeeResponse; - mWindowStyle = windowStyle; + mIsDialog = isDialog + setStyle(DialogFragment.STYLE_NO_TITLE, 0) + mUri = uri + this.startMillis = startMillis + this.endMillis = endMillis + mAttendeeResponseFromIntent = attendeeResponse + mWindowStyle = windowStyle } // This is currently required by the fragment manager. - 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, - endMillis, attendeeResponse, isDialog, windowStyle); - mEventId = eventId; + constructor() {} + constructor( + context: Context?, eventId: Long, startMillis: Long, endMillis: Long, + attendeeResponse: Int, isDialog: Boolean, windowStyle: Int + ) : this( + context, ContentUris.withAppendedId(Events.CONTENT_URI, eventId), startMillis, + endMillis, attendeeResponse, isDialog, windowStyle + ) { + this.eventId = eventId } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - + fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) if (mIsDialog) { - applyDialogParams(); + applyDialogParams() } - - final Activity activity = getActivity(); - mContext = activity; + val activity: Activity = getActivity() + mContext = activity } - private void applyDialogParams() { - Dialog dialog = getDialog(); - dialog.setCanceledOnTouchOutside(true); - - Window window = dialog.getWindow(); - window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); - - WindowManager.LayoutParams a = window.getAttributes(); - a.dimAmount = .4f; - - a.width = mDialogWidth; - a.height = mDialogHeight; + private fun applyDialogParams() { + val dialog: Dialog = getDialog() + dialog.setCanceledOnTouchOutside(true) + val window: Window = dialog.getWindow() + window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) + val a: WindowManager.LayoutParams = window.getAttributes() + a.dimAmount = .4f + a.width = mDialogWidth + a.height = mDialogHeight // On tablets , do smart positioning of dialog // On phones , use the whole screen - if (mX != -1 || mY != -1) { - a.x = mX - mDialogWidth / 2; - a.y = mY - mDialogHeight / 2; + a.x = mX - mDialogWidth / 2 + a.y = mY - mDialogHeight / 2 if (a.y < mMinTop) { - a.y = mMinTop + DIALOG_TOP_MARGIN; + a.y = mMinTop + DIALOG_TOP_MARGIN } - a.gravity = Gravity.LEFT | Gravity.TOP; + a.gravity = Gravity.LEFT or Gravity.TOP } - window.setAttributes(a); + window.setAttributes(a) } - public void setDialogParams(int x, int y, int minTop) { - mX = x; - mY = y; - mMinTop = minTop; + fun setDialogParams(x: Int, y: Int, minTop: Int) { + mX = x + mY = y + mMinTop = minTop } // Implements OnCheckedChangeListener @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - } - - public void onNothingSelected(AdapterView parent) { + fun onCheckedChanged(group: RadioGroup?, checkedId: Int) { } + fun onNothingSelected(parent: AdapterView<*>?) {} @Override - public void onDetach() { - super.onDetach(); - mController.deregisterEventHandler(R.layout.event_info); + fun onDetach() { + super.onDetach() + mController.deregisterEventHandler(R.layout.event_info) } @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mActivity = activity; + fun onAttach(activity: Activity?) { + super.onAttach(activity) + mActivity = activity // Ensure that mIsTabletConfig is set before creating the menu. - mIsTabletConfig = Utils.getConfigBool(mActivity, R.bool.tablet_config); - mController = CalendarController.getInstance(mActivity); - mController.registerEventHandler(R.layout.event_info, this); - + mIsTabletConfig = Utils.getConfigBool(mActivity, R.bool.tablet_config) + mController = CalendarController.getInstance(mActivity) + mController.registerEventHandler(R.layout.event_info, this) if (!mIsDialog) { - setHasOptionsMenu(true); + setHasOptionsMenu(true) } } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - if (mWindowStyle == DIALOG_WINDOW_STYLE) { - mView = inflater.inflate(R.layout.event_info_dialog, container, false); + fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + mView = if (mWindowStyle == DIALOG_WINDOW_STYLE) { + inflater.inflate(R.layout.event_info_dialog, container, false) } else { - mView = inflater.inflate(R.layout.event_info, container, false); - } - mScrollView = (ScrollView) mView.findViewById(R.id.event_info_scroll_view); - mLoadingMsgView = mView.findViewById(R.id.event_info_loading_msg); - mErrorMsgView = mView.findViewById(R.id.event_info_error_msg); - mTitle = (TextView) mView.findViewById(R.id.title); - mWhenDateTime = (TextView) mView.findViewById(R.id.when_datetime); - mWhere = (TextView) mView.findViewById(R.id.where); - mHeadlines = mView.findViewById(R.id.event_info_headline); - - mResponseRadioGroup = (RadioGroup) mView.findViewById(R.id.response_value); - - mAnimateAlpha = ObjectAnimator.ofFloat(mScrollView, "Alpha", 0, 1); - mAnimateAlpha.setDuration(FADE_IN_TIME); - mAnimateAlpha.addListener(new AnimatorListenerAdapter() { - int defLayerType; - + inflater.inflate(R.layout.event_info, container, false) + } + mScrollView = mView.findViewById(R.id.event_info_scroll_view) as ScrollView + mLoadingMsgView = mView.findViewById(R.id.event_info_loading_msg) + mErrorMsgView = mView.findViewById(R.id.event_info_error_msg) + mTitle = mView.findViewById(R.id.title) as TextView + mWhenDateTime = mView.findViewById(R.id.when_datetime) as TextView + mWhere = mView.findViewById(R.id.where) as TextView + mHeadlines = mView.findViewById(R.id.event_info_headline) + mResponseRadioGroup = mView.findViewById(R.id.response_value) as RadioGroup + mAnimateAlpha = ObjectAnimator.ofFloat(mScrollView, "Alpha", 0, 1) + mAnimateAlpha.setDuration(FADE_IN_TIME) + mAnimateAlpha.addListener(object : AnimatorListenerAdapter() { + var defLayerType = 0 @Override - public void onAnimationStart(Animator animation) { + fun onAnimationStart(animation: Animator?) { // Use hardware layer for better performance during animation - defLayerType = mScrollView.getLayerType(); - mScrollView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + defLayerType = mScrollView.getLayerType() + mScrollView.setLayerType(View.LAYER_TYPE_HARDWARE, null) // Ensure that the loading message is gone before showing the // event info - mLoadingMsgView.removeCallbacks(mLoadingMsgAlphaUpdater); - mLoadingMsgView.setVisibility(View.GONE); + mLoadingMsgView.removeCallbacks(mLoadingMsgAlphaUpdater) + mLoadingMsgView.setVisibility(View.GONE) } @Override - public void onAnimationCancel(Animator animation) { - mScrollView.setLayerType(defLayerType, null); + fun onAnimationCancel(animation: Animator?) { + mScrollView.setLayerType(defLayerType, null) } @Override - public void onAnimationEnd(Animator animation) { - mScrollView.setLayerType(defLayerType, null); + fun onAnimationEnd(animation: Animator?) { + mScrollView.setLayerType(defLayerType, null) // Do not cross fade after the first time - mNoCrossFade = true; + mNoCrossFade = true } - }); - - mLoadingMsgView.setAlpha(0); - mScrollView.setAlpha(0); - mErrorMsgView.setVisibility(View.INVISIBLE); - mLoadingMsgView.postDelayed(mLoadingMsgAlphaUpdater, LOADING_MSG_DELAY); + }) + mLoadingMsgView.setAlpha(0) + mScrollView.setAlpha(0) + mErrorMsgView.setVisibility(View.INVISIBLE) + mLoadingMsgView.postDelayed(mLoadingMsgAlphaUpdater, LOADING_MSG_DELAY) // 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); + if (!mIsDialog && !mIsTabletConfig || mWindowStyle == FULL_WINDOW_STYLE) { + mView.findViewById(R.id.event_info_buttons_container).setVisibility(View.GONE) } - - return mView; + return mView } - private void updateTitle() { - Resources res = getActivity().getResources(); - getActivity().setTitle(res.getString(R.string.event_info_title)); + private fun updateTitle() { + val res: Resources = getActivity().getResources() + getActivity().setTitle(res.getString(R.string.event_info_title)) } /** @@ -526,39 +497,39 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange * (and only) result from a query. * @return false if the cursor is empty, true otherwise */ - private boolean initEventCursor() { - if ((mEventCursor == null) || (mEventCursor.getCount() == 0)) { - return false; + private fun initEventCursor(): Boolean { + if (mEventCursor == null || mEventCursor.getCount() === 0) { + return false } - mEventCursor.moveToFirst(); - mEventId = mEventCursor.getInt(EVENT_INDEX_ID); - String rRule = mEventCursor.getString(EVENT_INDEX_RRULE); + mEventCursor.moveToFirst() + eventId = mEventCursor.getInt(EVENT_INDEX_ID) + val rRule: String = mEventCursor.getString(EVENT_INDEX_RRULE) // mHasAlarm will be true if it was saved in the event already. - mHasAlarm = (mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) == 1)? true : false; - return true; + mHasAlarm = if (mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) === 1) true else false + return true } @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); + fun onSaveInstanceState(outState: Bundle?) { + super.onSaveInstanceState(outState) } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); + fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) // Show color/edit/delete buttons only in non-dialog configuration - if (!mIsDialog && !mIsTabletConfig || mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) { - inflater.inflate(R.menu.event_info_title_bar, menu); - mMenu = menu; + if (!mIsDialog && !mIsTabletConfig || mWindowStyle == FULL_WINDOW_STYLE) { + inflater.inflate(R.menu.event_info_title_bar, menu) + mMenu = menu } } @Override - public boolean onOptionsItemSelected(MenuItem item) { + fun onOptionsItemSelected(item: MenuItem): Boolean { // If we're a dialog we don't want to handle menu buttons if (mIsDialog) { - return false; + return false } // Handles option menu selections: // Home button - close event info activity and start the main calendar @@ -567,32 +538,31 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange // activity // Delete button - start a delete query that calls a runnable that close // the info activity - - final int itemId = item.getItemId(); + val itemId: Int = item.getItemId() if (itemId == android.R.id.home) { - Utils.returnToCalendarHome(mContext); - mActivity.finish(); - return true; + Utils.returnToCalendarHome(mContext) + mActivity.finish() + return true } else if (itemId == R.id.info_action_edit) { - mActivity.finish(); + mActivity.finish() } - return super.onOptionsItemSelected(item); + return super.onOptionsItemSelected(item) } @Override - public void onStop() { - super.onStop(); + fun onStop() { + super.onStop() } @Override - public void onDestroy() { + fun onDestroy() { if (mEventCursor != null) { - mEventCursor.close(); + mEventCursor.close() } if (mCalendarsCursor != null) { - mCalendarsCursor.close(); + mCalendarsCursor.close() } - super.onDestroy(); + super.onDestroy() } /** @@ -603,275 +573,245 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange * @param eventId The recurring event. * @param status The new value for selfAttendeeStatus. */ - private void createExceptionResponse(long eventId, int status) { - ContentValues values = new ContentValues(); - values.put(Events.ORIGINAL_INSTANCE_TIME, mStartMillis); - values.put(Events.SELF_ATTENDEE_STATUS, status); - values.put(Events.STATUS, Events.STATUS_CONFIRMED); - - ArrayList ops = new ArrayList(); - Uri exceptionUri = Uri.withAppendedPath(Events.CONTENT_EXCEPTION_URI, - String.valueOf(eventId)); - ops.add(ContentProviderOperation.newInsert(exceptionUri).withValues(values).build()); - } - - public static int getResponseFromButtonId(int buttonId) { - return Attendees.ATTENDEE_STATUS_NONE; - } - - public static int findButtonIdForResponse(int response) { - return -1; - } - - private void displayEventNotFound() { - mErrorMsgView.setVisibility(View.VISIBLE); - mScrollView.setVisibility(View.GONE); - mLoadingMsgView.setVisibility(View.GONE); - } - - private void updateEvent(View view) { + private fun createExceptionResponse(eventId: Long, status: Int) { + val values = ContentValues() + values.put(Events.ORIGINAL_INSTANCE_TIME, startMillis) + values.put(Events.SELF_ATTENDEE_STATUS, status) + values.put(Events.STATUS, Events.STATUS_CONFIRMED) + val ops: ArrayList = ArrayList() + val exceptionUri: Uri = Uri.withAppendedPath( + Events.CONTENT_EXCEPTION_URI, + String.valueOf(eventId) + ) + ops.add(ContentProviderOperation.newInsert(exceptionUri).withValues(values).build()) + } + + private fun displayEventNotFound() { + mErrorMsgView.setVisibility(View.VISIBLE) + mScrollView.setVisibility(View.GONE) + mLoadingMsgView.setVisibility(View.GONE) + } + + private fun updateEvent(view: View?) { if (mEventCursor == null || view == null) { - return; - } - - Context context = view.getContext(); - if (context == null) { - return; + return } - - String eventName = mEventCursor.getString(EVENT_INDEX_TITLE); - if (eventName == null || eventName.length() == 0) { - eventName = getActivity().getString(R.string.no_title_label); + val context: Context = view.getContext() ?: return + var eventName: String = mEventCursor.getString(EVENT_INDEX_TITLE) + if (eventName == null || eventName.length() === 0) { + eventName = getActivity().getString(R.string.no_title_label) } // 3rd parties might not have specified the start/end time when firing the // Events.CONTENT_URI intent. Update these with values read from the db. - if (mStartMillis == 0 && mEndMillis == 0) { - mStartMillis = mEventCursor.getLong(EVENT_INDEX_DTSTART); - mEndMillis = mEventCursor.getLong(EVENT_INDEX_DTEND); - if (mEndMillis == 0) { - String duration = mEventCursor.getString(EVENT_INDEX_DURATION); + if (startMillis == 0L && endMillis == 0L) { + startMillis = mEventCursor.getLong(EVENT_INDEX_DTSTART) + endMillis = mEventCursor.getLong(EVENT_INDEX_DTEND) + if (endMillis == 0L) { + val duration: String = mEventCursor.getString(EVENT_INDEX_DURATION) if (!TextUtils.isEmpty(duration)) { try { - Duration d = new Duration(); - d.parse(duration); - long endMillis = mStartMillis + d.getMillis(); - if (endMillis >= mStartMillis) { - mEndMillis = endMillis; + val d = Duration() + d.parse(duration) + val endMillis: Long = startMillis + d.getMillis() + if (endMillis >= startMillis) { + this.endMillis = endMillis } else { - Log.d(TAG, "Invalid duration string: " + duration); + Log.d(TAG, "Invalid duration string: $duration") } - } catch (DateException e) { - Log.d(TAG, "Error parsing duration string " + duration, e); + } catch (e: DateException) { + Log.d(TAG, "Error parsing duration string $duration", e) } } - if (mEndMillis == 0) { - mEndMillis = mStartMillis; + if (endMillis == 0L) { + endMillis = startMillis } } } - - mAllDay = mEventCursor.getInt(EVENT_INDEX_ALL_DAY) != 0; - String location = mEventCursor.getString(EVENT_INDEX_EVENT_LOCATION); - String description = mEventCursor.getString(EVENT_INDEX_DESCRIPTION); - String rRule = mEventCursor.getString(EVENT_INDEX_RRULE); - String eventTimezone = mEventCursor.getString(EVENT_INDEX_EVENT_TIMEZONE); - - mHeadlines.setBackgroundColor(mCurrentColor); + mAllDay = mEventCursor.getInt(EVENT_INDEX_ALL_DAY) !== 0 + val location: String = mEventCursor.getString(EVENT_INDEX_EVENT_LOCATION) + val description: String = mEventCursor.getString(EVENT_INDEX_DESCRIPTION) + val rRule: String = mEventCursor.getString(EVENT_INDEX_RRULE) + val eventTimezone: String = mEventCursor.getString(EVENT_INDEX_EVENT_TIMEZONE) + mHeadlines.setBackgroundColor(mCurrentColor) // What if (eventName != null) { - setTextCommon(view, R.id.title, eventName); + setTextCommon(view, R.id.title, eventName) } // When // Set the date and repeats (if any) - String localTimezone = Utils.getTimeZone(mActivity, mTZUpdater); - - Resources resources = context.getResources(); - String displayedDatetime = Utils.getDisplayedDatetime(mStartMillis, mEndMillis, - System.currentTimeMillis(), localTimezone, mAllDay, context); - - String displayedTimezone = null; + val localTimezone: String = Utils.getTimeZone(mActivity, mTZUpdater) + val resources: Resources = context.getResources() + var displayedDatetime: String = Utils.getDisplayedDatetime( + startMillis, endMillis, + System.currentTimeMillis(), localTimezone, mAllDay, context + ) + var displayedTimezone: String? = null if (!mAllDay) { - displayedTimezone = Utils.getDisplayedTimezone(mStartMillis, localTimezone, - eventTimezone); + displayedTimezone = Utils.getDisplayedTimezone( + startMillis, localTimezone, + eventTimezone + ) } // Display the datetime. Make the timezone (if any) transparent. if (displayedTimezone == null) { - setTextCommon(view, R.id.when_datetime, displayedDatetime); + setTextCommon(view, R.id.when_datetime, displayedDatetime) } else { - int timezoneIndex = displayedDatetime.length(); - displayedDatetime += " " + displayedTimezone; - SpannableStringBuilder sb = new SpannableStringBuilder(displayedDatetime); - ForegroundColorSpan transparentColorSpan = new ForegroundColorSpan( - resources.getColor(R.color.event_info_headline_transparent_color)); - sb.setSpan(transparentColorSpan, timezoneIndex, displayedDatetime.length(), - Spannable.SPAN_INCLUSIVE_INCLUSIVE); - setTextCommon(view, R.id.when_datetime, sb); - } - - view.findViewById(R.id.when_repeat).setVisibility(View.GONE); + val timezoneIndex: Int = displayedDatetime.length() + displayedDatetime += " $displayedTimezone" + val sb = SpannableStringBuilder(displayedDatetime) + val transparentColorSpan = ForegroundColorSpan( + resources.getColor(R.color.event_info_headline_transparent_color) + ) + sb.setSpan( + transparentColorSpan, timezoneIndex, displayedDatetime.length(), + Spannable.SPAN_INCLUSIVE_INCLUSIVE + ) + setTextCommon(view, R.id.when_datetime, sb) + } + view.findViewById(R.id.when_repeat).setVisibility(View.GONE) // Organizer view is setup in the updateCalendar method // Where - if (location == null || location.trim().length() == 0) { - setVisibilityCommon(view, R.id.where, View.GONE); + if (location == null || location.trim().length() === 0) { + setVisibilityCommon(view, R.id.where, View.GONE) } else { - final TextView textView = mWhere; + val textView: TextView? = mWhere if (textView != null) { - textView.setText(location.trim()); + textView.setText(location.trim()) } } // Launch Custom App if (Utils.isJellybeanOrLater()) { - updateCustomAppButton(); + updateCustomAppButton() } } - private void updateCustomAppButton() { - setVisibilityCommon(mView, R.id.launch_custom_app_container, View.GONE); - return; + private fun updateCustomAppButton() { + setVisibilityCommon(mView, R.id.launch_custom_app_container, View.GONE) + return } - private void sendAccessibilityEvent() { - AccessibilityManager am = - (AccessibilityManager) getActivity().getSystemService(Service.ACCESSIBILITY_SERVICE); + private fun sendAccessibilityEvent() { + val am: AccessibilityManager = + getActivity().getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager if (!am.isEnabled()) { - return; - } - - AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED); - event.setClassName(EventInfoFragment.class.getName()); - event.setPackageName(getActivity().getPackageName()); - List text = event.getText(); - - if (mResponseRadioGroup.getVisibility() == View.VISIBLE) { - int id = mResponseRadioGroup.getCheckedRadioButtonId(); + return + } + val event: AccessibilityEvent = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED) + event.setClassName(EventInfoFragment::class.java.getName()) + event.setPackageName(getActivity().getPackageName()) + val text: List = event.getText() + if (mResponseRadioGroup.getVisibility() === View.VISIBLE) { + val id: Int = mResponseRadioGroup.getCheckedRadioButtonId() if (id != View.NO_ID) { - text.add(((TextView) getView().findViewById(R.id.response_label)).getText()); - text.add((((RadioButton) (mResponseRadioGroup.findViewById(id))) - .getText() + PERIOD_SPACE)); + text.add((getView().findViewById(R.id.response_label) as TextView).getText()) + text.add( + (mResponseRadioGroup.findViewById(id) as RadioButton) + .getText() + PERIOD_SPACE + ) } } - - am.sendAccessibilityEvent(event); + am.sendAccessibilityEvent(event) } - private void updateCalendar(View view) { - - mCalendarOwnerAccount = ""; + private fun updateCalendar(view: View) { + mCalendarOwnerAccount = "" if (mCalendarsCursor != null && mEventCursor != null) { - mCalendarsCursor.moveToFirst(); - String tempAccount = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT); - mCalendarOwnerAccount = (tempAccount == null) ? "" : tempAccount; - mOwnerCanRespond = mCalendarsCursor.getInt(CALENDARS_INDEX_OWNER_CAN_RESPOND) != 0; - mSyncAccountName = mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_NAME); - - setVisibilityCommon(view, R.id.organizer_container, View.GONE); + mCalendarsCursor.moveToFirst() + val tempAccount: String = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT) + mCalendarOwnerAccount = tempAccount ?: "" + mOwnerCanRespond = mCalendarsCursor.getInt(CALENDARS_INDEX_OWNER_CAN_RESPOND) !== 0 + mSyncAccountName = mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_NAME) + setVisibilityCommon(view, R.id.organizer_container, View.GONE) mIsBusyFreeCalendar = - mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) == Calendars.CAL_ACCESS_FREEBUSY; - + mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) === Calendars.CAL_ACCESS_FREEBUSY if (!mIsBusyFreeCalendar) { - - View b = mView.findViewById(R.id.edit); - b.setEnabled(true); - b.setOnClickListener(new OnClickListener() { + val b: View = mView.findViewById(R.id.edit) + b.setEnabled(true) + b.setOnClickListener(object : OnClickListener() { @Override - public void onClick(View v) { + fun onClick(v: View?) { // For dialogs, just close the fragment // For full screen, close activity on phone, leave it for tablet if (mIsDialog) { - EventInfoFragment.this.dismiss(); - } - else if (!mIsTabletConfig){ - getActivity().finish(); + this@EventInfoFragment.dismiss() + } else if (!mIsTabletConfig) { + getActivity().finish() } } - }); + }) } - View button; + var button: View if ((!mIsDialog && !mIsTabletConfig || - mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) && mMenu != null) { - mActivity.invalidateOptionsMenu(); + mWindowStyle == FULL_WINDOW_STYLE) && mMenu != null + ) { + mActivity.invalidateOptionsMenu() } } else { - setVisibilityCommon(view, R.id.calendar, View.GONE); - sendAccessibilityEventIfQueryDone(TOKEN_QUERY_DUPLICATE_CALENDARS); + setVisibilityCommon(view, R.id.calendar, View.GONE) + sendAccessibilityEventIfQueryDone(TOKEN_QUERY_DUPLICATE_CALENDARS) } } - private void setTextCommon(View view, int id, CharSequence text) { - TextView textView = (TextView) view.findViewById(id); - if (textView == null) - return; - textView.setText(text); + private fun setTextCommon(view: View, id: Int, text: CharSequence) { + val textView: TextView = view.findViewById(id) as TextView ?: return + textView.setText(text) } - private void setVisibilityCommon(View view, int id, int visibility) { - View v = view.findViewById(id); + private fun setVisibilityCommon(view: View?, id: Int, visibility: Int) { + val v: View = view.findViewById(id) if (v != null) { - v.setVisibility(visibility); + v.setVisibility(visibility) } - return; + return } @Override - public void onPause() { - mIsPaused = true; - super.onPause(); + fun onPause() { + mIsPaused = true + super.onPause() } @Override - public void onResume() { - super.onResume(); + fun onResume() { + super.onResume() if (mIsDialog) { - setDialogSize(getActivity().getResources()); - applyDialogParams(); + setDialogSize(getActivity().getResources()) + applyDialogParams() } - mIsPaused = false; + mIsPaused = false if (mTentativeUserSetResponse != Attendees.ATTENDEE_STATUS_NONE) { - int buttonId = findButtonIdForResponse(mTentativeUserSetResponse); - mResponseRadioGroup.check(buttonId); + val buttonId = findButtonIdForResponse(mTentativeUserSetResponse) + mResponseRadioGroup.check(buttonId) } } @Override - public void eventsChanged() { + fun eventsChanged() { } - @Override - public long getSupportedEventTypes() { - return EventType.EVENTS_CHANGED; - } + @get:Override val supportedEventTypes: Long + get() = EventType.EVENTS_CHANGED @Override - public void handleEvent(EventInfo event) { - reloadEvents(); - } - - public void reloadEvents() { + fun handleEvent(event: EventInfo?) { + reloadEvents() } + fun reloadEvents() {} @Override - public void onClick(View view) { + fun onClick(view: View?) { } - public long getEventId() { - return mEventId; - } - - public long getStartMillis() { - return mStartMillis; - } - public long getEndMillis() { - return mEndMillis; - } - private void setDialogSize(Resources r) { - mDialogWidth = (int)r.getDimension(R.dimen.event_info_dialog_width); - mDialogHeight = (int)r.getDimension(R.dimen.event_info_dialog_height); + private fun setDialogSize(r: Resources) { + mDialogWidth = r.getDimension(R.dimen.event_info_dialog_width) + mDialogHeight = r.getDimension(R.dimen.event_info_dialog_height) } -} +} \ No newline at end of file -- cgit v1.2.3 From 8b0a7714ee758e520ec6452617447ace8d3adee3 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 8 Jul 2021 20:43:27 +0000 Subject: AOSP/Calendar - EventInfoFragment fully converted with bp file This is the fully converted and touched-up Kotlin file after the converter was run once. All import statements were properly imported form the original Java file; however, there was one static import from the CalendarController class that did not translate correctly. Since CalendarController has already been converted to Kotlin, all static members are within a companion object, thus we must import "CalendarController.Companion" in order to have access to the static members of the class. Moreover, it seems that the "findViewById()" method was causing Kotlin build errors because the compiler could not infer generic type 'T', which is the return type of the aforementioned method according to android documentation. To solve this, the method must be called as: "findViewById()" where "View" may be replaced with the type of the variable invoking the method. Apart from these issues, the "override" modifier had to be manually added, and certain object references had to be made nullable. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.164 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 8m 30s Total aggregated tests run time: 8m 30s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.74 tests/sec [376 tests / 510540 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 6766 ms || clean = 2089 ms Total preparation time: 6s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 10m 10s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: Ia71f2cd9d30d38b4b1882c07b4fc2e380d457062 --- Android.bp | 1 + src/com/android/calendar/EventInfoFragment.kt | 350 ++++++++++++-------------- 2 files changed, 161 insertions(+), 190 deletions(-) diff --git a/Android.bp b/Android.bp index fd318cc3..ae0a3607 100644 --- a/Android.bp +++ b/Android.bp @@ -37,6 +37,7 @@ exclude_srcsd = [ "src/**/calendar/DayView.java", "src/**/calendar/Event.java", "src/**/calendar/EventInfoActivity.java", + "src/**/calendar/EventInfoFragment.java", "src/**/calendar/StickyHeaderListView.java", "src/**/calendar/Utils.java", ] diff --git a/src/com/android/calendar/EventInfoFragment.kt b/src/com/android/calendar/EventInfoFragment.kt index 816bff48..2f007ab5 100644 --- a/src/com/android/calendar/EventInfoFragment.kt +++ b/src/com/android/calendar/EventInfoFragment.kt @@ -15,54 +15,28 @@ */ package com.android.calendar -import android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY -import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME -import android.provider.CalendarContract.EXTRA_EVENT_END_TIME -import com.android.calendar.CalendarController.EVENT_EDIT_ON_LAUNCH import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ObjectAnimator import android.app.Activity import android.app.Dialog import android.app.DialogFragment -import android.app.FragmentManager import android.app.Service -import android.content.ActivityNotFoundException import android.content.ContentProviderOperation -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.SharedPreferences -import android.content.pm.ApplicationInfo -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 import android.os.Bundle -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.ContactsContract -import android.provider.ContactsContract.CommonDataKinds -import android.provider.ContactsContract.Intents -import android.provider.ContactsContract.QuickContact import android.text.Spannable import android.text.SpannableStringBuilder import android.text.TextUtils -import android.text.format.Time -import android.text.method.LinkMovementMethod -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 @@ -70,64 +44,52 @@ import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater import android.view.MenuItem -import android.view.MotionEvent import android.view.View import android.view.View.OnClickListener -import android.view.View.OnTouchListener import android.view.ViewGroup import android.view.Window import android.view.WindowManager import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityManager import android.widget.AdapterView -import android.widget.AdapterView.OnItemSelectedListener -import android.widget.Button -import android.widget.LinearLayout import android.widget.RadioButton import android.widget.RadioGroup import android.widget.RadioGroup.OnCheckedChangeListener import android.widget.ScrollView import android.widget.TextView -import android.widget.Toast import com.android.calendar.CalendarController.EventInfo import com.android.calendar.CalendarController.EventType -import com.android.calendar.alerts.QuickResponseActivity import com.android.calendarcommon2.DateException import com.android.calendarcommon2.Duration -import com.android.calendarcommon2.EventRecurrence -import com.android.colorpicker.HsvColorComparator import java.util.ArrayList -import java.util.Arrays -import java.util.Collections -import java.util.List class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarController.EventHandler, - OnClickListener { + OnClickListener { private var mWindowStyle = DIALOG_WINDOW_STYLE private var mCurrentQuery = 0 companion object { const val DEBUG = false const val TAG = "EventInfoFragment" - protected const val BUNDLE_KEY_EVENT_ID = "key_event_id" - protected const val BUNDLE_KEY_START_MILLIS = "key_start_millis" - protected const val BUNDLE_KEY_END_MILLIS = "key_end_millis" - protected const val BUNDLE_KEY_IS_DIALOG = "key_fragment_is_dialog" - protected const val BUNDLE_KEY_DELETE_DIALOG_VISIBLE = "key_delete_dialog_visible" - protected const val BUNDLE_KEY_WINDOW_STYLE = "key_window_style" - protected const val BUNDLE_KEY_CALENDAR_COLOR = "key_calendar_color" - protected const val BUNDLE_KEY_CALENDAR_COLOR_INIT = "key_calendar_color_init" - protected const val BUNDLE_KEY_CURRENT_COLOR = "key_current_color" - protected const val BUNDLE_KEY_CURRENT_COLOR_KEY = "key_current_color_key" - protected const val BUNDLE_KEY_CURRENT_COLOR_INIT = "key_current_color_init" - protected const val BUNDLE_KEY_ORIGINAL_COLOR = "key_original_color" - protected const val BUNDLE_KEY_ORIGINAL_COLOR_INIT = "key_original_color_init" - protected const val BUNDLE_KEY_ATTENDEE_RESPONSE = "key_attendee_response" - protected const val BUNDLE_KEY_USER_SET_ATTENDEE_RESPONSE = "key_user_set_attendee_response" - protected const val BUNDLE_KEY_TENTATIVE_USER_RESPONSE = "key_tentative_user_response" - protected const val BUNDLE_KEY_RESPONSE_WHICH_EVENTS = "key_response_which_events" - protected const val BUNDLE_KEY_REMINDER_MINUTES = "key_reminder_minutes" - protected const val BUNDLE_KEY_REMINDER_METHODS = "key_reminder_methods" + internal const val BUNDLE_KEY_EVENT_ID = "key_event_id" + internal const val BUNDLE_KEY_START_MILLIS = "key_start_millis" + internal const val BUNDLE_KEY_END_MILLIS = "key_end_millis" + internal const val BUNDLE_KEY_IS_DIALOG = "key_fragment_is_dialog" + internal const val BUNDLE_KEY_DELETE_DIALOG_VISIBLE = "key_delete_dialog_visible" + internal const val BUNDLE_KEY_WINDOW_STYLE = "key_window_style" + internal const val BUNDLE_KEY_CALENDAR_COLOR = "key_calendar_color" + internal const val BUNDLE_KEY_CALENDAR_COLOR_INIT = "key_calendar_color_init" + internal const val BUNDLE_KEY_CURRENT_COLOR = "key_current_color" + internal const val BUNDLE_KEY_CURRENT_COLOR_KEY = "key_current_color_key" + internal const val BUNDLE_KEY_CURRENT_COLOR_INIT = "key_current_color_init" + internal const val BUNDLE_KEY_ORIGINAL_COLOR = "key_original_color" + internal const val BUNDLE_KEY_ORIGINAL_COLOR_INIT = "key_original_color_init" + internal const val BUNDLE_KEY_ATTENDEE_RESPONSE = "key_attendee_response" + internal const val BUNDLE_KEY_USER_SET_ATTENDEE_RESPONSE = "key_user_set_attendee_response" + internal const val BUNDLE_KEY_TENTATIVE_USER_RESPONSE = "key_tentative_user_response" + internal const val BUNDLE_KEY_RESPONSE_WHICH_EVENTS = "key_response_which_events" + internal const val BUNDLE_KEY_REMINDER_MINUTES = "key_reminder_minutes" + internal const val BUNDLE_KEY_REMINDER_METHODS = "key_reminder_methods" private const val PERIOD_SPACE = ". " private const val NO_EVENT_COLOR = "" @@ -151,31 +113,31 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr private const val TOKEN_QUERY_VISIBLE_CALENDARS = 1 shl 5 private const val TOKEN_QUERY_COLORS = 1 shl 6 private const val TOKEN_QUERY_ALL = (TOKEN_QUERY_DUPLICATE_CALENDARS - or TOKEN_QUERY_ATTENDEES or TOKEN_QUERY_CALENDARS or TOKEN_QUERY_EVENT - or TOKEN_QUERY_REMINDERS or TOKEN_QUERY_VISIBLE_CALENDARS or TOKEN_QUERY_COLORS) + or TOKEN_QUERY_ATTENDEES or TOKEN_QUERY_CALENDARS or TOKEN_QUERY_EVENT + or TOKEN_QUERY_REMINDERS or TOKEN_QUERY_VISIBLE_CALENDARS or TOKEN_QUERY_COLORS) private val EVENT_PROJECTION = arrayOf( - Events._ID, // 0 do not remove; used in DeleteEventHelper + Events._ID, // 0 do not remove; used in DeleteEventHelper Events.TITLE, // 1 do not remove; used in DeleteEventHelper Events.RRULE, // 2 do not remove; used in DeleteEventHelper - Events.ALL_DAY, // 3 do not remove; used in DeleteEventHelper - Events.CALENDAR_ID, // 4 do not remove; used in DeleteEventHelper - Events.DTSTART, // 5 do not remove; used in DeleteEventHelper - Events._SYNC_ID, // 6 do not remove; used in DeleteEventHelper - Events.EVENT_TIMEZONE, // 7 do not remove; used in DeleteEventHelper - Events.DESCRIPTION, // 8 - Events.EVENT_LOCATION, // 9 - Calendars.CALENDAR_ACCESS_LEVEL, // 10 - Events.CALENDAR_COLOR, // 11 - Events.EVENT_COLOR, // 12 - Events.HAS_ATTENDEE_DATA, // 13 + Events.ALL_DAY, // 3 do not remove; used in DeleteEventHelper + Events.CALENDAR_ID, // 4 do not remove; used in DeleteEventHelper + Events.DTSTART, // 5 do not remove; used in DeleteEventHelper + Events._SYNC_ID, // 6 do not remove; used in DeleteEventHelper + Events.EVENT_TIMEZONE, // 7 do not remove; used in DeleteEventHelper + Events.DESCRIPTION, // 8 + Events.EVENT_LOCATION, // 9 + Calendars.CALENDAR_ACCESS_LEVEL, // 10 + 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 + 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 const val EVENT_INDEX_ID = 0 @@ -201,11 +163,11 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr private const val EVENT_INDEX_DTEND = 20 private const val EVENT_INDEX_DURATION = 21 val CALENDARS_PROJECTION = arrayOf( - Calendars._ID, // 0 - Calendars.CALENDAR_DISPLAY_NAME, // 1 - Calendars.OWNER_ACCOUNT, // 2 - Calendars.CAN_ORGANIZER_RESPOND, // 3 - Calendars.ACCOUNT_NAME, // 4 + Calendars._ID, // 0 + Calendars.CALENDAR_DISPLAY_NAME, // 1 + Calendars.OWNER_ACCOUNT, // 2 + Calendars.CAN_ORGANIZER_RESPOND, // 3 + Calendars.ACCOUNT_NAME, // 4 Calendars.ACCOUNT_TYPE // 5 ) const val CALENDARS_INDEX_DISPLAY_NAME = 1 @@ -292,20 +254,20 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr private var mResponseRadioGroup: RadioGroup? = null var mToEmails: ArrayList = ArrayList() var mCcEmails: ArrayList = ArrayList() - private val mTZUpdater: Runnable = object : Runnable() { + private val mTZUpdater: Runnable = object : Runnable { @Override - fun run() { + override fun run() { updateEvent(mView) } } - private val mLoadingMsgAlphaUpdater: Runnable = object : Runnable() { + private val mLoadingMsgAlphaUpdater: Runnable = object : Runnable { @Override - fun run() { + override fun run() { // Since this is run after a delay, make sure to only show the message // if the event's data is not shown yet. - if (!mAnimateAlpha.isRunning() && mScrollView.getAlpha() === 0) { + if (!mAnimateAlpha!!.isRunning() && mScrollView!!.getAlpha() == 0f) { mLoadingMsgStartTime = System.currentTimeMillis() - mLoadingMsgView.setAlpha(1) + mLoadingMsgView?.setAlpha(1f) } } } @@ -314,8 +276,7 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr private val mDismissOnResume = false private var mX = -1 private var mY = -1 - private var mMinTop // Dialog cannot be above this location - = 0 + private var mMinTop = 0 // Dialog cannot be above this location private var mIsTabletConfig = false private var mActivity: Activity? = null private var mContext: Context? = null @@ -328,8 +289,13 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr } constructor( - context: Context, uri: Uri?, startMillis: Long, endMillis: Long, - attendeeResponse: Int, isDialog: Boolean, windowStyle: Int + context: Context, + uri: Uri?, + startMillis: Long, + endMillis: Long, + attendeeResponse: Int, + isDialog: Boolean, + windowStyle: Int ) { val r: Resources = context.getResources() if (mScale == 0f) { @@ -356,17 +322,22 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr // This is currently required by the fragment manager. constructor() {} constructor( - context: Context?, eventId: Long, startMillis: Long, endMillis: Long, - attendeeResponse: Int, isDialog: Boolean, windowStyle: Int + context: Context?, + eventId: Long, + startMillis: Long, + endMillis: Long, + attendeeResponse: Int, + isDialog: Boolean, + windowStyle: Int ) : this( - context, ContentUris.withAppendedId(Events.CONTENT_URI, eventId), startMillis, + context as Context, ContentUris.withAppendedId(Events.CONTENT_URI, eventId), startMillis, endMillis, attendeeResponse, isDialog, windowStyle ) { this.eventId = eventId } @Override - fun onActivityCreated(savedInstanceState: Bundle?) { + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) if (mIsDialog) { applyDialogParams() @@ -378,25 +349,24 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr private fun applyDialogParams() { val dialog: Dialog = getDialog() dialog.setCanceledOnTouchOutside(true) - val window: Window = dialog.getWindow() - window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) - val a: WindowManager.LayoutParams = window.getAttributes() - a.dimAmount = .4f - a.width = mDialogWidth - a.height = mDialogHeight - + val window: Window? = dialog.getWindow() + window?.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) + val a: WindowManager.LayoutParams? = window?.getAttributes() + a!!.dimAmount = .4f + a!!.width = mDialogWidth + a!!.height = mDialogHeight // On tablets , do smart positioning of dialog // On phones , use the whole screen if (mX != -1 || mY != -1) { - a.x = mX - mDialogWidth / 2 - a.y = mY - mDialogHeight / 2 - if (a.y < mMinTop) { - a.y = mMinTop + DIALOG_TOP_MARGIN + a!!.x = mX - mDialogWidth / 2 + a!!.y = mY - mDialogHeight / 2 + if (a!!.y < mMinTop) { + a!!.y = mMinTop + DIALOG_TOP_MARGIN } - a.gravity = Gravity.LEFT or Gravity.TOP + a!!.gravity = Gravity.LEFT or Gravity.TOP } - window.setAttributes(a) + window?.setAttributes(a) } fun setDialogParams(x: Int, y: Int, minTop: Int) { @@ -407,32 +377,33 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr // Implements OnCheckedChangeListener @Override - fun onCheckedChanged(group: RadioGroup?, checkedId: Int) { + override fun onCheckedChanged(group: RadioGroup?, checkedId: Int) { } fun onNothingSelected(parent: AdapterView<*>?) {} @Override - fun onDetach() { + override fun onDetach() { super.onDetach() - mController.deregisterEventHandler(R.layout.event_info) + mController?.deregisterEventHandler(R.layout.event_info) } @Override - fun onAttach(activity: Activity?) { + override fun onAttach(activity: Activity?) { super.onAttach(activity) mActivity = activity // Ensure that mIsTabletConfig is set before creating the menu. - mIsTabletConfig = Utils.getConfigBool(mActivity, R.bool.tablet_config) + mIsTabletConfig = Utils.getConfigBool(mActivity as Context, R.bool.tablet_config) mController = CalendarController.getInstance(mActivity) - mController.registerEventHandler(R.layout.event_info, this) + mController?.registerEventHandler(R.layout.event_info, this) if (!mIsDialog) { setHasOptionsMenu(true) } } @Override - fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, savedInstanceState: Bundle? ): View? { mView = if (mWindowStyle == DIALOG_WINDOW_STYLE) { @@ -440,49 +411,49 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr } else { inflater.inflate(R.layout.event_info, container, false) } - mScrollView = mView.findViewById(R.id.event_info_scroll_view) as ScrollView - mLoadingMsgView = mView.findViewById(R.id.event_info_loading_msg) - mErrorMsgView = mView.findViewById(R.id.event_info_error_msg) - mTitle = mView.findViewById(R.id.title) as TextView - mWhenDateTime = mView.findViewById(R.id.when_datetime) as TextView - mWhere = mView.findViewById(R.id.where) as TextView - mHeadlines = mView.findViewById(R.id.event_info_headline) - mResponseRadioGroup = mView.findViewById(R.id.response_value) as RadioGroup - mAnimateAlpha = ObjectAnimator.ofFloat(mScrollView, "Alpha", 0, 1) - mAnimateAlpha.setDuration(FADE_IN_TIME) - mAnimateAlpha.addListener(object : AnimatorListenerAdapter() { + mScrollView = mView?.findViewById(R.id.event_info_scroll_view) as ScrollView + mLoadingMsgView = mView?.findViewById(R.id.event_info_loading_msg) + mErrorMsgView = mView?.findViewById(R.id.event_info_error_msg) + mTitle = mView?.findViewById(R.id.title) as TextView + mWhenDateTime = mView?.findViewById(R.id.when_datetime) as TextView + mWhere = mView?.findViewById(R.id.where) as TextView + mHeadlines = mView?.findViewById(R.id.event_info_headline) + mResponseRadioGroup = mView?.findViewById(R.id.response_value) as RadioGroup + mAnimateAlpha = ObjectAnimator.ofFloat(mScrollView, "Alpha", 0f, 1f) + mAnimateAlpha?.setDuration(FADE_IN_TIME.toLong()) + mAnimateAlpha?.addListener(object : AnimatorListenerAdapter() { var defLayerType = 0 @Override - fun onAnimationStart(animation: Animator?) { + override fun onAnimationStart(animation: Animator?) { // Use hardware layer for better performance during animation - defLayerType = mScrollView.getLayerType() - mScrollView.setLayerType(View.LAYER_TYPE_HARDWARE, null) + defLayerType = mScrollView?.getLayerType() as Int + mScrollView?.setLayerType(View.LAYER_TYPE_HARDWARE, null) // Ensure that the loading message is gone before showing the // event info - mLoadingMsgView.removeCallbacks(mLoadingMsgAlphaUpdater) - mLoadingMsgView.setVisibility(View.GONE) + mLoadingMsgView?.removeCallbacks(mLoadingMsgAlphaUpdater) + mLoadingMsgView?.setVisibility(View.GONE) } @Override - fun onAnimationCancel(animation: Animator?) { - mScrollView.setLayerType(defLayerType, null) + override fun onAnimationCancel(animation: Animator?) { + mScrollView?.setLayerType(defLayerType, null) } @Override - fun onAnimationEnd(animation: Animator?) { - mScrollView.setLayerType(defLayerType, null) + override fun onAnimationEnd(animation: Animator?) { + mScrollView?.setLayerType(defLayerType, null) // Do not cross fade after the first time mNoCrossFade = true } }) - mLoadingMsgView.setAlpha(0) - mScrollView.setAlpha(0) - mErrorMsgView.setVisibility(View.INVISIBLE) - mLoadingMsgView.postDelayed(mLoadingMsgAlphaUpdater, LOADING_MSG_DELAY) + mLoadingMsgView?.setAlpha(0f) + mScrollView?.setAlpha(0f) + mErrorMsgView?.setVisibility(View.INVISIBLE) + mLoadingMsgView?.postDelayed(mLoadingMsgAlphaUpdater, LOADING_MSG_DELAY.toLong()) // Hide Edit/Delete buttons if in full screen mode on a phone if (!mIsDialog && !mIsTabletConfig || mWindowStyle == FULL_WINDOW_STYLE) { - mView.findViewById(R.id.event_info_buttons_container).setVisibility(View.GONE) + mView?.findViewById(R.id.event_info_buttons_container)?.setVisibility(View.GONE) } return mView } @@ -502,7 +473,7 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr return false } mEventCursor.moveToFirst() - eventId = mEventCursor.getInt(EVENT_INDEX_ID) + eventId = mEventCursor.getInt(EVENT_INDEX_ID).toLong() val rRule: String = mEventCursor.getString(EVENT_INDEX_RRULE) // mHasAlarm will be true if it was saved in the event already. mHasAlarm = if (mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) === 1) true else false @@ -510,12 +481,12 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr } @Override - fun onSaveInstanceState(outState: Bundle?) { + override fun onSaveInstanceState(outState: Bundle?) { super.onSaveInstanceState(outState) } @Override - fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater) { + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) // Show color/edit/delete buttons only in non-dialog configuration if (!mIsDialog && !mIsTabletConfig || mWindowStyle == FULL_WINDOW_STYLE) { @@ -525,7 +496,7 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr } @Override - fun onOptionsItemSelected(item: MenuItem): Boolean { + override fun onOptionsItemSelected(item: MenuItem): Boolean { // If we're a dialog we don't want to handle menu buttons if (mIsDialog) { @@ -540,22 +511,22 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr // the info activity val itemId: Int = item.getItemId() if (itemId == android.R.id.home) { - Utils.returnToCalendarHome(mContext) - mActivity.finish() + Utils.returnToCalendarHome(mContext as Context) + mActivity?.finish() return true } else if (itemId == R.id.info_action_edit) { - mActivity.finish() + mActivity?.finish() } return super.onOptionsItemSelected(item) } @Override - fun onStop() { + override fun onStop() { super.onStop() } @Override - fun onDestroy() { + override fun onDestroy() { if (mEventCursor != null) { mEventCursor.close() } @@ -581,15 +552,15 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr val ops: ArrayList = ArrayList() val exceptionUri: Uri = Uri.withAppendedPath( Events.CONTENT_EXCEPTION_URI, - String.valueOf(eventId) + eventId.toString() ) ops.add(ContentProviderOperation.newInsert(exceptionUri).withValues(values).build()) } private fun displayEventNotFound() { - mErrorMsgView.setVisibility(View.VISIBLE) - mScrollView.setVisibility(View.GONE) - mLoadingMsgView.setVisibility(View.GONE) + mErrorMsgView?.setVisibility(View.VISIBLE) + mScrollView?.setVisibility(View.GONE) + mLoadingMsgView?.setVisibility(View.GONE) } private fun updateEvent(view: View?) { @@ -598,7 +569,7 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr } val context: Context = view.getContext() ?: return var eventName: String = mEventCursor.getString(EVENT_INDEX_TITLE) - if (eventName == null || eventName.length() === 0) { + if (eventName == null || eventName.length == 0) { eventName = getActivity().getString(R.string.no_title_label) } @@ -633,7 +604,7 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr val description: String = mEventCursor.getString(EVENT_INDEX_DESCRIPTION) val rRule: String = mEventCursor.getString(EVENT_INDEX_RRULE) val eventTimezone: String = mEventCursor.getString(EVENT_INDEX_EVENT_TIMEZONE) - mHeadlines.setBackgroundColor(mCurrentColor) + mHeadlines?.setBackgroundColor(mCurrentColor) // What if (eventName != null) { @@ -642,11 +613,11 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr // When // Set the date and repeats (if any) - val localTimezone: String = Utils.getTimeZone(mActivity, mTZUpdater) + val localTimezone: String? = Utils.getTimeZone(mActivity, mTZUpdater) val resources: Resources = context.getResources() - var displayedDatetime: String = Utils.getDisplayedDatetime( + var displayedDatetime: String? = Utils.getDisplayedDatetime( startMillis, endMillis, - System.currentTimeMillis(), localTimezone, mAllDay, context + System.currentTimeMillis(), localTimezone as String, mAllDay, context ) var displayedTimezone: String? = null if (!mAllDay) { @@ -657,27 +628,26 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr } // Display the datetime. Make the timezone (if any) transparent. if (displayedTimezone == null) { - setTextCommon(view, R.id.when_datetime, displayedDatetime) + setTextCommon(view, R.id.when_datetime, displayedDatetime as CharSequence) } else { - val timezoneIndex: Int = displayedDatetime.length() + val timezoneIndex: Int = displayedDatetime!!.length displayedDatetime += " $displayedTimezone" val sb = SpannableStringBuilder(displayedDatetime) val transparentColorSpan = ForegroundColorSpan( resources.getColor(R.color.event_info_headline_transparent_color) ) sb.setSpan( - transparentColorSpan, timezoneIndex, displayedDatetime.length(), + transparentColorSpan, timezoneIndex, displayedDatetime!!.length, Spannable.SPAN_INCLUSIVE_INCLUSIVE ) setTextCommon(view, R.id.when_datetime, sb) } - view.findViewById(R.id.when_repeat).setVisibility(View.GONE) + view.findViewById(R.id.when_repeat).setVisibility(View.GONE) // Organizer view is setup in the updateCalendar method - // Where - if (location == null || location.trim().length() === 0) { + if (location == null || location.trim().length == 0) { setVisibilityCommon(view, R.id.where, View.GONE) } else { val textView: TextView? = mWhere @@ -707,21 +677,21 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED) event.setClassName(EventInfoFragment::class.java.getName()) event.setPackageName(getActivity().getPackageName()) - val text: List = event.getText() - if (mResponseRadioGroup.getVisibility() === View.VISIBLE) { - val id: Int = mResponseRadioGroup.getCheckedRadioButtonId() + var text = event.getText() + if (mResponseRadioGroup?.getVisibility() == View.VISIBLE) { + val id: Int = mResponseRadioGroup!!.getCheckedRadioButtonId() if (id != View.NO_ID) { - text.add((getView().findViewById(R.id.response_label) as TextView).getText()) + text.add((getView()?.findViewById(R.id.response_label) as TextView)?.getText()) text.add( - (mResponseRadioGroup.findViewById(id) as RadioButton) - .getText() + PERIOD_SPACE + (mResponseRadioGroup?.findViewById(id) as RadioButton) + .getText().toString() + PERIOD_SPACE ) } } am.sendAccessibilityEvent(event) } - private fun updateCalendar(view: View) { + private fun updateCalendar(view: View?) { mCalendarOwnerAccount = "" if (mCalendarsCursor != null && mEventCursor != null) { mCalendarsCursor.moveToFirst() @@ -733,11 +703,11 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr mIsBusyFreeCalendar = mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) === Calendars.CAL_ACCESS_FREEBUSY if (!mIsBusyFreeCalendar) { - val b: View = mView.findViewById(R.id.edit) - b.setEnabled(true) - b.setOnClickListener(object : OnClickListener() { + val b: View? = mView?.findViewById(R.id.edit) + b?.setEnabled(true) + b?.setOnClickListener(object : OnClickListener { @Override - fun onClick(v: View?) { + override fun onClick(v: View?) { // For dialogs, just close the fragment // For full screen, close activity on phone, leave it for tablet if (mIsDialog) { @@ -750,9 +720,9 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr } var button: View if ((!mIsDialog && !mIsTabletConfig || - mWindowStyle == FULL_WINDOW_STYLE) && mMenu != null + mWindowStyle == FULL_WINDOW_STYLE) && mMenu != null ) { - mActivity.invalidateOptionsMenu() + mActivity?.invalidateOptionsMenu() } } else { setVisibilityCommon(view, R.id.calendar, View.GONE) @@ -766,7 +736,7 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr } private fun setVisibilityCommon(view: View?, id: Int, visibility: Int) { - val v: View = view.findViewById(id) + val v: View? = view?.findViewById(id) if (v != null) { v.setVisibility(visibility) } @@ -774,13 +744,13 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr } @Override - fun onPause() { + override fun onPause() { mIsPaused = true super.onPause() } @Override - fun onResume() { + override fun onResume() { super.onResume() if (mIsDialog) { setDialogSize(getActivity().getResources()) @@ -789,29 +759,29 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr mIsPaused = false if (mTentativeUserSetResponse != Attendees.ATTENDEE_STATUS_NONE) { val buttonId = findButtonIdForResponse(mTentativeUserSetResponse) - mResponseRadioGroup.check(buttonId) + mResponseRadioGroup?.check(buttonId) } } @Override - fun eventsChanged() { + override fun eventsChanged() { } - @get:Override val supportedEventTypes: Long + @get:Override override val supportedEventTypes: Long get() = EventType.EVENTS_CHANGED @Override - fun handleEvent(event: EventInfo?) { + override fun handleEvent(event: EventInfo?) { reloadEvents() } fun reloadEvents() {} @Override - fun onClick(view: View?) { + override fun onClick(view: View?) { } private fun setDialogSize(r: Resources) { - mDialogWidth = r.getDimension(R.dimen.event_info_dialog_width) - mDialogHeight = r.getDimension(R.dimen.event_info_dialog_height) + mDialogWidth = r.getDimension(R.dimen.event_info_dialog_width).toInt() + mDialogHeight = r.getDimension(R.dimen.event_info_dialog_height).toInt() } } \ No newline at end of file -- cgit v1.2.3 From ba5bef48f57609cbdfaae3f424741a48d3e0456a Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 9 Jul 2021 14:05:13 +0000 Subject: AOSP/Calendar - Copy of MonthByWeekAdapter.java and SimpleWeeksAdapter.java The Java code in MonthByWeekAdapter.java and SimpleWeeksAdapter.java has been copied into corresponding .kt files. Test: manual - opening both .java and .kt shows that they are identical. Change-Id: Ifda8edf7caf80084c45265ecf4a57de5fa62fbdc --- .../android/calendar/month/MonthByWeekAdapter.kt | 406 +++++++++++++++++++++ .../android/calendar/month/SimpleWeeksAdapter.kt | 302 +++++++++++++++ 2 files changed, 708 insertions(+) create mode 100644 src/com/android/calendar/month/MonthByWeekAdapter.kt create mode 100644 src/com/android/calendar/month/SimpleWeeksAdapter.kt diff --git a/src/com/android/calendar/month/MonthByWeekAdapter.kt b/src/com/android/calendar/month/MonthByWeekAdapter.kt new file mode 100644 index 00000000..45a1bea1 --- /dev/null +++ b/src/com/android/calendar/month/MonthByWeekAdapter.kt @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2010 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.month; + +import android.content.Context; +import android.content.res.Configuration; +import android.os.Handler; +import android.os.Message; +import android.text.format.Time; +import android.util.Log; +import android.view.GestureDetector; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.widget.AbsListView.LayoutParams; + +import com.android.calendar.CalendarController; +import com.android.calendar.CalendarController.EventType; +import com.android.calendar.CalendarController.ViewType; +import com.android.calendar.Event; +import com.android.calendar.R; +import com.android.calendar.Utils; + +import java.util.ArrayList; +import java.util.HashMap; + +public class MonthByWeekAdapter extends SimpleWeeksAdapter { + private static final String TAG = "MonthByWeekAdapter"; + + public static final String WEEK_PARAMS_IS_MINI = "mini_month"; + protected static int DEFAULT_QUERY_DAYS = 7 * 8; // 8 weeks + private static final long ANIMATE_TODAY_TIMEOUT = 1000; + + protected CalendarController mController; + protected String mHomeTimeZone; + protected Time mTempTime; + protected Time mToday; + protected int mFirstJulianDay; + protected int mQueryDays; + protected boolean mIsMiniMonth = true; + protected int mOrientation = Configuration.ORIENTATION_LANDSCAPE; + private final boolean mShowAgendaWithMonth; + + protected ArrayList> mEventDayList = new ArrayList>(); + protected ArrayList mEvents = null; + + private boolean mAnimateToday = false; + private long mAnimateTime = 0; + + private Handler mEventDialogHandler; + + MonthWeekEventsView mClickedView; + MonthWeekEventsView mSingleTapUpView; + MonthWeekEventsView mLongClickedView; + + float mClickedXLocation; // Used to find which day was clicked + long mClickTime; // Used to calculate minimum click animation time + // Used to insure minimal time for seeing the click animation before switching views + private static final int mOnTapDelay = 100; + // Minimal time for a down touch action before stating the click animation, this insures that + // there is no click animation on flings + private static int mOnDownDelay; + private static int mTotalClickDelay; + // Minimal distance to move the finger in order to cancel the click animation + private static float mMovedPixelToCancel; + + public MonthByWeekAdapter(Context context, HashMap params) { + super(context, params); + if (params.containsKey(WEEK_PARAMS_IS_MINI)) { + mIsMiniMonth = params.get(WEEK_PARAMS_IS_MINI) != 0; + } + mShowAgendaWithMonth = Utils.getConfigBool(context, R.bool.show_agenda_with_month); + ViewConfiguration vc = ViewConfiguration.get(context); + mOnDownDelay = ViewConfiguration.getTapTimeout(); + mMovedPixelToCancel = vc.getScaledTouchSlop(); + mTotalClickDelay = mOnDownDelay + mOnTapDelay; + } + + public void animateToday() { + mAnimateToday = true; + mAnimateTime = System.currentTimeMillis(); + } + + @Override + protected void init() { + super.init(); + mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener()); + mController = CalendarController.getInstance(mContext); + mHomeTimeZone = Utils.getTimeZone(mContext, null); + mSelectedDay.switchTimezone(mHomeTimeZone); + mToday = new Time(mHomeTimeZone); + mToday.setToNow(); + mTempTime = new Time(mHomeTimeZone); + } + + private void updateTimeZones() { + mSelectedDay.timezone = mHomeTimeZone; + mSelectedDay.normalize(true); + mToday.timezone = mHomeTimeZone; + mToday.setToNow(); + mTempTime.switchTimezone(mHomeTimeZone); + } + + @Override + public void setSelectedDay(Time selectedTime) { + mSelectedDay.set(selectedTime); + long millis = mSelectedDay.normalize(true); + mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( + Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek); + notifyDataSetChanged(); + } + + public void setEvents(int firstJulianDay, int numDays, ArrayList events) { + if (mIsMiniMonth) { + if (Log.isLoggable(TAG, Log.ERROR)) { + Log.e(TAG, "Attempted to set events for mini view. Events only supported in full" + + " view."); + } + return; + } + mEvents = events; + mFirstJulianDay = firstJulianDay; + mQueryDays = numDays; + // Create a new list, this is necessary since the weeks are referencing + // pieces of the old list + ArrayList> eventDayList = new ArrayList>(); + for (int i = 0; i < numDays; i++) { + eventDayList.add(new ArrayList()); + } + + if (events == null || events.size() == 0) { + if(Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "No events. Returning early--go schedule something fun."); + } + mEventDayList = eventDayList; + refresh(); + return; + } + + // Compute the new set of days with events + for (Event event : events) { + int startDay = event.startDay - mFirstJulianDay; + int endDay = event.endDay - mFirstJulianDay + 1; + if (startDay < numDays || endDay >= 0) { + if (startDay < 0) { + startDay = 0; + } + if (startDay > numDays) { + continue; + } + if (endDay < 0) { + continue; + } + if (endDay > numDays) { + endDay = numDays; + } + for (int j = startDay; j < endDay; j++) { + eventDayList.get(j).add(event); + } + } + } + if(Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Processed " + events.size() + " events."); + } + mEventDayList = eventDayList; + refresh(); + } + + @SuppressWarnings("unchecked") + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (mIsMiniMonth) { + return super.getView(position, convertView, parent); + } + MonthWeekEventsView v; + LayoutParams params = new LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + HashMap drawingParams = null; + boolean isAnimatingToday = false; + if (convertView != null) { + v = (MonthWeekEventsView) convertView; + // Checking updateToday uses the current params instead of the new + // params, so this is assuming the view is relatively stable + if (mAnimateToday && v.updateToday(mSelectedDay.timezone)) { + long currentTime = System.currentTimeMillis(); + // If it's been too long since we tried to start the animation + // don't show it. This can happen if the user stops a scroll + // before reaching today. + if (currentTime - mAnimateTime > ANIMATE_TODAY_TIMEOUT) { + mAnimateToday = false; + mAnimateTime = 0; + } else { + isAnimatingToday = true; + // There is a bug that causes invalidates to not work some + // of the time unless we recreate the view. + v = new MonthWeekEventsView(mContext); + } + } else { + drawingParams = (HashMap) v.getTag(); + } + } else { + v = new MonthWeekEventsView(mContext); + } + if (drawingParams == null) { + drawingParams = new HashMap(); + } + drawingParams.clear(); + + v.setLayoutParams(params); + v.setClickable(true); + v.setOnTouchListener(this); + + int selectedDay = -1; + if (mSelectedWeek == position) { + selectedDay = mSelectedDay.weekDay; + } + + drawingParams.put(SimpleWeekView.VIEW_PARAMS_HEIGHT, + (parent.getHeight() + parent.getTop()) / mNumWeeks); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, mShowWeekNumber ? 1 : 0); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth); + drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ORIENTATION, mOrientation); + + if (isAnimatingToday) { + drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ANIMATE_TODAY, 1); + mAnimateToday = false; + } + + v.setWeekParams(drawingParams, mSelectedDay.timezone); + return v; + } + + @Override + protected void refresh() { + mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); + mShowWeekNumber = Utils.getShowWeekNumber(mContext); + mHomeTimeZone = Utils.getTimeZone(mContext, null); + mOrientation = mContext.getResources().getConfiguration().orientation; + updateTimeZones(); + notifyDataSetChanged(); + } + + @Override + protected void onDayTapped(Time day) { + setDayParameters(day); + if (mShowAgendaWithMonth || mIsMiniMonth) { + // If agenda view is visible with month view , refresh the views + // with the selected day's info + mController.sendEvent(mContext, EventType.GO_TO, day, day, -1, + ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null); + } else { + // Else , switch to the detailed view + mController.sendEvent(mContext, EventType.GO_TO, day, day, -1, + ViewType.DETAIL, + CalendarController.EXTRA_GOTO_DATE + | CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS, null, null); + } + } + + private void setDayParameters(Time day) { + day.timezone = mHomeTimeZone; + Time currTime = new Time(mHomeTimeZone); + currTime.set(mController.getTime()); + day.hour = currTime.hour; + day.minute = currTime.minute; + day.allDay = false; + day.normalize(true); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (!(v instanceof MonthWeekEventsView)) { + return super.onTouch(v, event); + } + + int action = event.getAction(); + + // Event was tapped - switch to the detailed view making sure the click animation + // is done first. + if (mGestureDetector.onTouchEvent(event)) { + mSingleTapUpView = (MonthWeekEventsView) v; + long delay = System.currentTimeMillis() - mClickTime; + // Make sure the animation is visible for at least mOnTapDelay - mOnDownDelay ms + mListView.postDelayed(mDoSingleTapUp, + delay > mTotalClickDelay ? 0 : mTotalClickDelay - delay); + return true; + } else { + // Animate a click - on down: show the selected day in the "clicked" color. + // On Up/scroll/move/cancel: hide the "clicked" color. + switch (action) { + case MotionEvent.ACTION_DOWN: + mClickedView = (MonthWeekEventsView)v; + mClickedXLocation = event.getX(); + mClickTime = System.currentTimeMillis(); + mListView.postDelayed(mDoClick, mOnDownDelay); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_SCROLL: + case MotionEvent.ACTION_CANCEL: + clearClickedView((MonthWeekEventsView)v); + break; + case MotionEvent.ACTION_MOVE: + // No need to cancel on vertical movement, ACTION_SCROLL will do that. + if (Math.abs(event.getX() - mClickedXLocation) > mMovedPixelToCancel) { + clearClickedView((MonthWeekEventsView)v); + } + break; + default: + break; + } + } + // Do not tell the frameworks we consumed the touch action so that fling actions can be + // processed by the fragment. + return false; + } + + /** + * This is here so we can identify events and process them + */ + protected class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { + @Override + public boolean onSingleTapUp(MotionEvent e) { + return true; + } + + @Override + public void onLongPress(MotionEvent e) { + if (mLongClickedView != null) { + Time day = mLongClickedView.getDayFromLocation(mClickedXLocation); + if (day != null) { + mLongClickedView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + Message message = new Message(); + message.obj = day; + } + mLongClickedView.clearClickedDay(); + mLongClickedView = null; + } + } + } + + // Clear the visual cues of the click animation and related running code. + private void clearClickedView(MonthWeekEventsView v) { + mListView.removeCallbacks(mDoClick); + synchronized(v) { + v.clearClickedDay(); + } + mClickedView = null; + } + + // Perform the tap animation in a runnable to allow a delay before showing the tap color. + // This is done to prevent a click animation when a fling is done. + private final Runnable mDoClick = new Runnable() { + @Override + public void run() { + if (mClickedView != null) { + synchronized(mClickedView) { + mClickedView.setClickedDay(mClickedXLocation); + } + mLongClickedView = mClickedView; + mClickedView = null; + // This is a workaround , sometimes the top item on the listview doesn't refresh on + // invalidate, so this forces a re-draw. + mListView.invalidate(); + } + } + }; + + // Performs the single tap operation: go to the tapped day. + // This is done in a runnable to allow the click animation to finish before switching views + private final Runnable mDoSingleTapUp = new Runnable() { + @Override + public void run() { + if (mSingleTapUpView != null) { + Time day = mSingleTapUpView.getDayFromLocation(mClickedXLocation); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Touched day at Row=" + mSingleTapUpView.mWeek + " day=" + day.toString()); + } + if (day != null) { + onDayTapped(day); + } + clearClickedView(mSingleTapUpView); + mSingleTapUpView = null; + } + } + }; +} diff --git a/src/com/android/calendar/month/SimpleWeeksAdapter.kt b/src/com/android/calendar/month/SimpleWeeksAdapter.kt new file mode 100644 index 00000000..d29b2622 --- /dev/null +++ b/src/com/android/calendar/month/SimpleWeeksAdapter.kt @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2010 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.month; + +// TODO Remove calendar imports when the required methods have been +// refactored into the public api +import com.android.calendar.CalendarController; +import com.android.calendar.Utils; + +import android.content.Context; +import android.text.format.Time; +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import android.view.ViewGroup; +import android.widget.AbsListView.LayoutParams; +import android.widget.BaseAdapter; +import android.widget.ListView; + +import java.util.Calendar; +import java.util.HashMap; +import java.util.Locale; + +/** + *

+ * This is a specialized adapter for creating a list of weeks with selectable + * days. It can be configured to display the week number, start the week on a + * given day, show a reduced number of days, or display an arbitrary number of + * weeks at a time. See {@link SimpleDayPickerFragment} for usage. + *

+ */ +public class SimpleWeeksAdapter extends BaseAdapter implements OnTouchListener { + + private static final String TAG = "MonthByWeek"; + + /** + * The number of weeks to display at a time. + */ + public static final String WEEK_PARAMS_NUM_WEEKS = "num_weeks"; + /** + * Which month should be in focus currently. + */ + public static final String WEEK_PARAMS_FOCUS_MONTH = "focus_month"; + /** + * Whether the week number should be shown. Non-zero to show them. + */ + public static final String WEEK_PARAMS_SHOW_WEEK = "week_numbers"; + /** + * Which day the week should start on. {@link Time#SUNDAY} through + * {@link Time#SATURDAY}. + */ + public static final String WEEK_PARAMS_WEEK_START = "week_start"; + /** + * The Julian day to highlight as selected. + */ + public static final String WEEK_PARAMS_JULIAN_DAY = "selected_day"; + /** + * How many days of the week to display [1-7]. + */ + public static final String WEEK_PARAMS_DAYS_PER_WEEK = "days_per_week"; + + protected static final int WEEK_COUNT = CalendarController.MAX_CALENDAR_WEEK + - CalendarController.MIN_CALENDAR_WEEK; + protected static int DEFAULT_NUM_WEEKS = 6; + protected static int DEFAULT_MONTH_FOCUS = 0; + protected static int DEFAULT_DAYS_PER_WEEK = 7; + protected static int DEFAULT_WEEK_HEIGHT = 32; + protected static int WEEK_7_OVERHANG_HEIGHT = 7; + + protected static float mScale = 0; + protected Context mContext; + // The day to highlight as selected + protected Time mSelectedDay; + // The week since 1970 that the selected day is in + protected int mSelectedWeek; + // When the week starts; numbered like Time. (e.g. SUNDAY=0). + protected int mFirstDayOfWeek; + protected boolean mShowWeekNumber = false; + protected GestureDetector mGestureDetector; + protected int mNumWeeks = DEFAULT_NUM_WEEKS; + protected int mDaysPerWeek = DEFAULT_DAYS_PER_WEEK; + protected int mFocusMonth = DEFAULT_MONTH_FOCUS; + + public SimpleWeeksAdapter(Context context, HashMap params) { + mContext = context; + + // Get default week start based on locale, subtracting one for use with android Time. + Calendar cal = Calendar.getInstance(Locale.getDefault()); + mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1; + + if (mScale == 0) { + mScale = context.getResources().getDisplayMetrics().density; + if (mScale != 1) { + WEEK_7_OVERHANG_HEIGHT *= mScale; + } + } + init(); + updateParams(params); + } + + /** + * Set up the gesture detector and selected time + */ + protected void init() { + mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener()); + mSelectedDay = new Time(); + mSelectedDay.setToNow(); + } + + /** + * Parse the parameters and set any necessary fields. See + * {@link #WEEK_PARAMS_NUM_WEEKS} for parameter details. + * + * @param params A list of parameters for this adapter + */ + public void updateParams(HashMap params) { + if (params == null) { + Log.e(TAG, "WeekParameters are null! Cannot update adapter."); + return; + } + if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) { + mFocusMonth = params.get(WEEK_PARAMS_FOCUS_MONTH); + } + if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) { + mNumWeeks = params.get(WEEK_PARAMS_NUM_WEEKS); + } + if (params.containsKey(WEEK_PARAMS_SHOW_WEEK)) { + mShowWeekNumber = params.get(WEEK_PARAMS_SHOW_WEEK) != 0; + } + if (params.containsKey(WEEK_PARAMS_WEEK_START)) { + mFirstDayOfWeek = params.get(WEEK_PARAMS_WEEK_START); + } + if (params.containsKey(WEEK_PARAMS_JULIAN_DAY)) { + int julianDay = params.get(WEEK_PARAMS_JULIAN_DAY); + mSelectedDay.setJulianDay(julianDay); + mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay(julianDay, mFirstDayOfWeek); + } + if (params.containsKey(WEEK_PARAMS_DAYS_PER_WEEK)) { + mDaysPerWeek = params.get(WEEK_PARAMS_DAYS_PER_WEEK); + } + refresh(); + } + + /** + * Updates the selected day and related parameters. + * + * @param selectedTime The time to highlight + */ + public void setSelectedDay(Time selectedTime) { + mSelectedDay.set(selectedTime); + long millis = mSelectedDay.normalize(true); + mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( + Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek); + notifyDataSetChanged(); + } + + /** + * Returns the currently highlighted day + * + * @return + */ + public Time getSelectedDay() { + return mSelectedDay; + } + + /** + * updates any config options that may have changed and refreshes the view + */ + protected void refresh() { + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return WEEK_COUNT; + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return position; + } + + @SuppressWarnings("unchecked") + @Override + public View getView(int position, View convertView, ViewGroup parent) { + SimpleWeekView v; + HashMap drawingParams = null; + if (convertView != null) { + v = (SimpleWeekView) convertView; + // We store the drawing parameters in the view so it can be recycled + drawingParams = (HashMap) v.getTag(); + } else { + v = new SimpleWeekView(mContext); + // Set up the new view + LayoutParams params = new LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + v.setLayoutParams(params); + v.setClickable(true); + v.setOnTouchListener(this); + } + if (drawingParams == null) { + drawingParams = new HashMap(); + } + drawingParams.clear(); + + int selectedDay = -1; + if (mSelectedWeek == position) { + selectedDay = mSelectedDay.weekDay; + } + + // pass in all the view parameters + drawingParams.put(SimpleWeekView.VIEW_PARAMS_HEIGHT, + (parent.getHeight() - WEEK_7_OVERHANG_HEIGHT) / mNumWeeks); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, mShowWeekNumber ? 1 : 0); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position); + drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth); + v.setWeekParams(drawingParams, mSelectedDay.timezone); + v.invalidate(); + + return v; + } + + /** + * Changes which month is in focus and updates the view. + * + * @param month The month to show as in focus [0-11] + */ + public void updateFocusMonth(int month) { + mFocusMonth = month; + notifyDataSetChanged(); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (mGestureDetector.onTouchEvent(event)) { + SimpleWeekView view = (SimpleWeekView) v; + Time day = ((SimpleWeekView)v).getDayFromLocation(event.getX()); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Touched day at Row=" + view.mWeek + " day=" + day.toString()); + } + if (day != null) { + onDayTapped(day); + } + return true; + } + return false; + } + + /** + * Maintains the same hour/min/sec but moves the day to the tapped day. + * + * @param day The day that was tapped + */ + protected void onDayTapped(Time day) { + day.hour = mSelectedDay.hour; + day.minute = mSelectedDay.minute; + day.second = mSelectedDay.second; + setSelectedDay(day); + } + + + /** + * This is here so we can identify single tap events and set the selected + * day correctly + */ + protected class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { + @Override + public boolean onSingleTapUp(MotionEvent e) { + return true; + } + } + + ListView mListView; + + public void setListView(ListView lv) { + mListView = lv; + } +} -- cgit v1.2.3 From 83732c13ab8f01ceb3ffa9a7990d773c9abb28e2 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 9 Jul 2021 14:13:01 +0000 Subject: AOSP/Calendar - Initial conversion of MonthByWeekAdapter and SimpleWeeksAdapter This is how Android Studio's built-in converter converted MonthByWeekAdapter.java and SimpleWeeksAdapter.java to Kotlin without any additional edits. Change-Id: Ic727bcfd5bef2b1e70dc6c96ddea398b881ad5ec --- .../android/calendar/month/MonthByWeekAdapter.kt | 521 ++++++++++----------- .../android/calendar/month/SimpleWeeksAdapter.kt | 364 +++++++------- 2 files changed, 440 insertions(+), 445 deletions(-) diff --git a/src/com/android/calendar/month/MonthByWeekAdapter.kt b/src/com/android/calendar/month/MonthByWeekAdapter.kt index 45a1bea1..a0ae1b1b 100644 --- a/src/com/android/calendar/month/MonthByWeekAdapter.kt +++ b/src/com/android/calendar/month/MonthByWeekAdapter.kt @@ -13,394 +13,391 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar.month; - -import android.content.Context; -import android.content.res.Configuration; -import android.os.Handler; -import android.os.Message; -import android.text.format.Time; -import android.util.Log; -import android.view.GestureDetector; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.widget.AbsListView.LayoutParams; - -import com.android.calendar.CalendarController; -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.CalendarController.ViewType; -import com.android.calendar.Event; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.HashMap; - -public class MonthByWeekAdapter extends SimpleWeeksAdapter { - private static final String TAG = "MonthByWeekAdapter"; - - public static final String WEEK_PARAMS_IS_MINI = "mini_month"; - protected static int DEFAULT_QUERY_DAYS = 7 * 8; // 8 weeks - private static final long ANIMATE_TODAY_TIMEOUT = 1000; - - protected CalendarController mController; - protected String mHomeTimeZone; - protected Time mTempTime; - protected Time mToday; - protected int mFirstJulianDay; - protected int mQueryDays; - protected boolean mIsMiniMonth = true; - protected int mOrientation = Configuration.ORIENTATION_LANDSCAPE; - private final boolean mShowAgendaWithMonth; - - protected ArrayList> mEventDayList = new ArrayList>(); - protected ArrayList mEvents = null; - - private boolean mAnimateToday = false; - private long mAnimateTime = 0; - - private Handler mEventDialogHandler; - - MonthWeekEventsView mClickedView; - MonthWeekEventsView mSingleTapUpView; - MonthWeekEventsView mLongClickedView; - - float mClickedXLocation; // Used to find which day was clicked - long mClickTime; // Used to calculate minimum click animation time - // Used to insure minimal time for seeing the click animation before switching views - private static final int mOnTapDelay = 100; - // Minimal time for a down touch action before stating the click animation, this insures that - // there is no click animation on flings - private static int mOnDownDelay; - private static int mTotalClickDelay; - // Minimal distance to move the finger in order to cancel the click animation - private static float mMovedPixelToCancel; - - public MonthByWeekAdapter(Context context, HashMap params) { - super(context, params); - if (params.containsKey(WEEK_PARAMS_IS_MINI)) { - mIsMiniMonth = params.get(WEEK_PARAMS_IS_MINI) != 0; - } - mShowAgendaWithMonth = Utils.getConfigBool(context, R.bool.show_agenda_with_month); - ViewConfiguration vc = ViewConfiguration.get(context); - mOnDownDelay = ViewConfiguration.getTapTimeout(); - mMovedPixelToCancel = vc.getScaledTouchSlop(); - mTotalClickDelay = mOnDownDelay + mOnTapDelay; - } - - public void animateToday() { - mAnimateToday = true; - mAnimateTime = System.currentTimeMillis(); +package com.android.calendar.month + +import android.content.Context +import android.content.res.Configuration +import android.os.Handler +import android.os.Message +import android.text.format.Time +import android.util.Log +import android.view.GestureDetector +import android.view.HapticFeedbackConstants +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration +import android.view.ViewGroup +import android.widget.AbsListView.LayoutParams +import com.android.calendar.CalendarController +import com.android.calendar.CalendarController.EventType +import com.android.calendar.CalendarController.ViewType +import com.android.calendar.Event +import com.android.calendar.R +import com.android.calendar.Utils +import java.util.ArrayList +import java.util.HashMap + +class MonthByWeekAdapter(context: Context?, params: HashMap) : + SimpleWeeksAdapter(context, params) { + protected var mController: CalendarController? = null + protected var mHomeTimeZone: String? = null + protected var mTempTime: Time? = null + protected var mToday: Time? = null + protected var mFirstJulianDay = 0 + protected var mQueryDays = 0 + protected var mIsMiniMonth = true + protected var mOrientation: Int = Configuration.ORIENTATION_LANDSCAPE + private val mShowAgendaWithMonth: Boolean + protected var mEventDayList: ArrayList> = ArrayList>() + protected var mEvents: ArrayList? = null + private var mAnimateToday = false + private var mAnimateTime: Long = 0 + private val mEventDialogHandler: Handler? = null + var mClickedView: MonthWeekEventsView? = null + var mSingleTapUpView: MonthWeekEventsView? = null + var mLongClickedView: MonthWeekEventsView? = null + var mClickedXLocation // Used to find which day was clicked + = 0f + var mClickTime // Used to calculate minimum click animation time + : Long = 0 + + fun animateToday() { + mAnimateToday = true + mAnimateTime = System.currentTimeMillis() } @Override - protected void init() { - super.init(); - mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener()); - mController = CalendarController.getInstance(mContext); - mHomeTimeZone = Utils.getTimeZone(mContext, null); - mSelectedDay.switchTimezone(mHomeTimeZone); - mToday = new Time(mHomeTimeZone); - mToday.setToNow(); - mTempTime = new Time(mHomeTimeZone); + protected fun init() { + super.init() + mGestureDetector = GestureDetector(mContext, CalendarGestureListener()) + mController = CalendarController.getInstance(mContext) + mHomeTimeZone = Utils.getTimeZone(mContext, null) + mSelectedDay.switchTimezone(mHomeTimeZone) + mToday = Time(mHomeTimeZone) + mToday.setToNow() + mTempTime = Time(mHomeTimeZone) } - private void updateTimeZones() { - mSelectedDay.timezone = mHomeTimeZone; - mSelectedDay.normalize(true); - mToday.timezone = mHomeTimeZone; - mToday.setToNow(); - mTempTime.switchTimezone(mHomeTimeZone); + private fun updateTimeZones() { + mSelectedDay.timezone = mHomeTimeZone + mSelectedDay.normalize(true) + mToday.timezone = mHomeTimeZone + mToday.setToNow() + mTempTime.switchTimezone(mHomeTimeZone) } @Override - public void setSelectedDay(Time selectedTime) { - mSelectedDay.set(selectedTime); - long millis = mSelectedDay.normalize(true); + fun setSelectedDay(selectedTime: Time?) { + mSelectedDay.set(selectedTime) + val millis: Long = mSelectedDay.normalize(true) mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek); - notifyDataSetChanged(); + Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek + ) + notifyDataSetChanged() } - public void setEvents(int firstJulianDay, int numDays, ArrayList events) { + fun setEvents(firstJulianDay: Int, numDays: Int, events: ArrayList?) { if (mIsMiniMonth) { if (Log.isLoggable(TAG, Log.ERROR)) { - Log.e(TAG, "Attempted to set events for mini view. Events only supported in full" - + " view."); + Log.e( + TAG, "Attempted to set events for mini view. Events only supported in full" + + " view." + ) } - return; + return } - mEvents = events; - mFirstJulianDay = firstJulianDay; - mQueryDays = numDays; + mEvents = events + mFirstJulianDay = firstJulianDay + mQueryDays = numDays // Create a new list, this is necessary since the weeks are referencing // pieces of the old list - ArrayList> eventDayList = new ArrayList>(); - for (int i = 0; i < numDays; i++) { - eventDayList.add(new ArrayList()); + val eventDayList: ArrayList> = ArrayList>() + for (i in 0 until numDays) { + eventDayList.add(ArrayList()) } - - if (events == null || events.size() == 0) { - if(Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "No events. Returning early--go schedule something fun."); + if (events == null || events.size() === 0) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "No events. Returning early--go schedule something fun.") } - mEventDayList = eventDayList; - refresh(); - return; + mEventDayList = eventDayList + refresh() + return } // Compute the new set of days with events - for (Event event : events) { - int startDay = event.startDay - mFirstJulianDay; - int endDay = event.endDay - mFirstJulianDay + 1; + for (event in events) { + var startDay: Int = event.startDay - mFirstJulianDay + var endDay: Int = event.endDay - mFirstJulianDay + 1 if (startDay < numDays || endDay >= 0) { if (startDay < 0) { - startDay = 0; + startDay = 0 } if (startDay > numDays) { - continue; + continue } if (endDay < 0) { - continue; + continue } if (endDay > numDays) { - endDay = numDays; + endDay = numDays } - for (int j = startDay; j < endDay; j++) { - eventDayList.get(j).add(event); + for (j in startDay until endDay) { + eventDayList.get(j).add(event) } } } - if(Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Processed " + events.size() + " events."); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Processed " + events.size().toString() + " events.") } - mEventDayList = eventDayList; - refresh(); + mEventDayList = eventDayList + refresh() } @SuppressWarnings("unchecked") @Override - public View getView(int position, View convertView, ViewGroup parent) { + fun getView(position: Int, convertView: View?, parent: ViewGroup): View { if (mIsMiniMonth) { - return super.getView(position, convertView, parent); + return super.getView(position, convertView, parent) } - MonthWeekEventsView v; - LayoutParams params = new LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - HashMap drawingParams = null; - boolean isAnimatingToday = false; + var v: MonthWeekEventsView + val params = LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT + ) + var drawingParams: HashMap? = null + var isAnimatingToday = false if (convertView != null) { - v = (MonthWeekEventsView) convertView; + v = convertView as MonthWeekEventsView // Checking updateToday uses the current params instead of the new // params, so this is assuming the view is relatively stable if (mAnimateToday && v.updateToday(mSelectedDay.timezone)) { - long currentTime = System.currentTimeMillis(); + val currentTime: Long = System.currentTimeMillis() // If it's been too long since we tried to start the animation // don't show it. This can happen if the user stops a scroll // before reaching today. if (currentTime - mAnimateTime > ANIMATE_TODAY_TIMEOUT) { - mAnimateToday = false; - mAnimateTime = 0; + mAnimateToday = false + mAnimateTime = 0 } else { - isAnimatingToday = true; + isAnimatingToday = true // There is a bug that causes invalidates to not work some // of the time unless we recreate the view. - v = new MonthWeekEventsView(mContext); - } + v = MonthWeekEventsView(mContext) + } } else { - drawingParams = (HashMap) v.getTag(); + drawingParams = v.getTag() as HashMap } } else { - v = new MonthWeekEventsView(mContext); + v = MonthWeekEventsView(mContext) } if (drawingParams == null) { - drawingParams = new HashMap(); + drawingParams = HashMap() } - drawingParams.clear(); - - v.setLayoutParams(params); - v.setClickable(true); - v.setOnTouchListener(this); - - int selectedDay = -1; - if (mSelectedWeek == position) { - selectedDay = mSelectedDay.weekDay; + drawingParams.clear() + v.setLayoutParams(params) + v.setClickable(true) + v.setOnTouchListener(this) + var selectedDay = -1 + if (mSelectedWeek === position) { + selectedDay = mSelectedDay.weekDay } - - drawingParams.put(SimpleWeekView.VIEW_PARAMS_HEIGHT, - (parent.getHeight() + parent.getTop()) / mNumWeeks); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, mShowWeekNumber ? 1 : 0); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth); - drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ORIENTATION, mOrientation); - + drawingParams.put( + SimpleWeekView.VIEW_PARAMS_HEIGHT, + (parent.getHeight() + parent.getTop()) / mNumWeeks + ) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, if (mShowWeekNumber) 1 else 0) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth) + drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ORIENTATION, mOrientation) if (isAnimatingToday) { - drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ANIMATE_TODAY, 1); - mAnimateToday = false; + drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ANIMATE_TODAY, 1) + mAnimateToday = false } - - v.setWeekParams(drawingParams, mSelectedDay.timezone); - return v; + v.setWeekParams(drawingParams, mSelectedDay.timezone) + return v } @Override - protected void refresh() { - mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - mShowWeekNumber = Utils.getShowWeekNumber(mContext); - mHomeTimeZone = Utils.getTimeZone(mContext, null); - mOrientation = mContext.getResources().getConfiguration().orientation; - updateTimeZones(); - notifyDataSetChanged(); + protected fun refresh() { + mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext) + mShowWeekNumber = Utils.getShowWeekNumber(mContext) + mHomeTimeZone = Utils.getTimeZone(mContext, null) + mOrientation = mContext.getResources().getConfiguration().orientation + updateTimeZones() + notifyDataSetChanged() } @Override - protected void onDayTapped(Time day) { - setDayParameters(day); - if (mShowAgendaWithMonth || mIsMiniMonth) { + protected fun onDayTapped(day: Time) { + setDayParameters(day) + if (mShowAgendaWithMonth || mIsMiniMonth) { // If agenda view is visible with month view , refresh the views // with the selected day's info - mController.sendEvent(mContext, EventType.GO_TO, day, day, -1, - ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null); + mController.sendEvent( + mContext, EventType.GO_TO, day, day, -1, + ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null + ) } else { // Else , switch to the detailed view - mController.sendEvent(mContext, EventType.GO_TO, day, day, -1, - ViewType.DETAIL, - CalendarController.EXTRA_GOTO_DATE - | CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS, null, null); + mController.sendEvent( + mContext, EventType.GO_TO, day, day, -1, + ViewType.DETAIL, CalendarController.EXTRA_GOTO_DATE + or CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS, null, null + ) } } - private void setDayParameters(Time day) { - day.timezone = mHomeTimeZone; - Time currTime = new Time(mHomeTimeZone); - currTime.set(mController.getTime()); - day.hour = currTime.hour; - day.minute = currTime.minute; - day.allDay = false; - day.normalize(true); + private fun setDayParameters(day: Time) { + day.timezone = mHomeTimeZone + val currTime = Time(mHomeTimeZone) + currTime.set(mController.getTime()) + day.hour = currTime.hour + day.minute = currTime.minute + day.allDay = false + day.normalize(true) } @Override - public boolean onTouch(View v, MotionEvent event) { - if (!(v instanceof MonthWeekEventsView)) { - return super.onTouch(v, event); + fun onTouch(v: View?, event: MotionEvent): Boolean { + if (v !is MonthWeekEventsView) { + return super.onTouch(v, event) } - - int action = event.getAction(); + val action: Int = event.getAction() // Event was tapped - switch to the detailed view making sure the click animation // is done first. if (mGestureDetector.onTouchEvent(event)) { - mSingleTapUpView = (MonthWeekEventsView) v; - long delay = System.currentTimeMillis() - mClickTime; + mSingleTapUpView = v as MonthWeekEventsView? + val delay: Long = System.currentTimeMillis() - mClickTime // Make sure the animation is visible for at least mOnTapDelay - mOnDownDelay ms - mListView.postDelayed(mDoSingleTapUp, - delay > mTotalClickDelay ? 0 : mTotalClickDelay - delay); - return true; + mListView.postDelayed( + mDoSingleTapUp, + if (delay > mTotalClickDelay) 0 else mTotalClickDelay - delay + ) + return true } else { // Animate a click - on down: show the selected day in the "clicked" color. // On Up/scroll/move/cancel: hide the "clicked" color. - switch (action) { - case MotionEvent.ACTION_DOWN: - mClickedView = (MonthWeekEventsView)v; - mClickedXLocation = event.getX(); - mClickTime = System.currentTimeMillis(); - mListView.postDelayed(mDoClick, mOnDownDelay); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_SCROLL: - case MotionEvent.ACTION_CANCEL: - clearClickedView((MonthWeekEventsView)v); - break; - case MotionEvent.ACTION_MOVE: - // No need to cancel on vertical movement, ACTION_SCROLL will do that. + when (action) { + MotionEvent.ACTION_DOWN -> { + mClickedView = v as MonthWeekEventsView + mClickedXLocation = event.getX() + mClickTime = System.currentTimeMillis() + mListView.postDelayed(mDoClick, mOnDownDelay) + } + MotionEvent.ACTION_UP, MotionEvent.ACTION_SCROLL, MotionEvent.ACTION_CANCEL -> clearClickedView( + v as MonthWeekEventsView? + ) + MotionEvent.ACTION_MOVE -> // No need to cancel on vertical movement, ACTION_SCROLL will do that. if (Math.abs(event.getX() - mClickedXLocation) > mMovedPixelToCancel) { - clearClickedView((MonthWeekEventsView)v); + clearClickedView(v as MonthWeekEventsView?) } - break; - default: - break; + else -> { + } } } // Do not tell the frameworks we consumed the touch action so that fling actions can be // processed by the fragment. - return false; + return false } /** * This is here so we can identify events and process them */ - protected class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { + protected inner class CalendarGestureListener : GestureDetector.SimpleOnGestureListener() { @Override - public boolean onSingleTapUp(MotionEvent e) { - return true; + fun onSingleTapUp(e: MotionEvent?): Boolean { + return true } @Override - public void onLongPress(MotionEvent e) { + fun onLongPress(e: MotionEvent?) { if (mLongClickedView != null) { - Time day = mLongClickedView.getDayFromLocation(mClickedXLocation); + val day: Time = mLongClickedView.getDayFromLocation(mClickedXLocation) if (day != null) { - mLongClickedView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - Message message = new Message(); - message.obj = day; + mLongClickedView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) + val message = Message() + message.obj = day } - mLongClickedView.clearClickedDay(); - mLongClickedView = null; - } + mLongClickedView.clearClickedDay() + mLongClickedView = null + } } } // Clear the visual cues of the click animation and related running code. - private void clearClickedView(MonthWeekEventsView v) { - mListView.removeCallbacks(mDoClick); - synchronized(v) { - v.clearClickedDay(); - } - mClickedView = null; + private fun clearClickedView(v: MonthWeekEventsView?) { + mListView.removeCallbacks(mDoClick) + synchronized(v) { v.clearClickedDay() } + mClickedView = null } // Perform the tap animation in a runnable to allow a delay before showing the tap color. // This is done to prevent a click animation when a fling is done. - private final Runnable mDoClick = new Runnable() { + private val mDoClick: Runnable = object : Runnable() { @Override - public void run() { + fun run() { if (mClickedView != null) { - synchronized(mClickedView) { - mClickedView.setClickedDay(mClickedXLocation); - } - mLongClickedView = mClickedView; - mClickedView = null; + synchronized(mClickedView) { mClickedView.setClickedDay(mClickedXLocation) } + mLongClickedView = mClickedView + mClickedView = null // This is a workaround , sometimes the top item on the listview doesn't refresh on // invalidate, so this forces a re-draw. - mListView.invalidate(); + mListView.invalidate() } } - }; + } // Performs the single tap operation: go to the tapped day. // This is done in a runnable to allow the click animation to finish before switching views - private final Runnable mDoSingleTapUp = new Runnable() { + private val mDoSingleTapUp: Runnable = object : Runnable() { @Override - public void run() { + fun run() { if (mSingleTapUpView != null) { - Time day = mSingleTapUpView.getDayFromLocation(mClickedXLocation); + val day: Time = mSingleTapUpView.getDayFromLocation(mClickedXLocation) if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Touched day at Row=" + mSingleTapUpView.mWeek + " day=" + day.toString()); + Log.d( + TAG, + "Touched day at Row=" + mSingleTapUpView.mWeek.toString() + " day=" + day.toString() + ) } if (day != null) { - onDayTapped(day); + onDayTapped(day) } - clearClickedView(mSingleTapUpView); - mSingleTapUpView = null; + clearClickedView(mSingleTapUpView) + mSingleTapUpView = null } } - }; -} + } + + companion object { + private const val TAG = "MonthByWeekAdapter" + const val WEEK_PARAMS_IS_MINI = "mini_month" + protected var DEFAULT_QUERY_DAYS = 7 * 8 // 8 weeks + private const val ANIMATE_TODAY_TIMEOUT: Long = 1000 + + // Used to insure minimal time for seeing the click animation before switching views + private const val mOnTapDelay = 100 + + // Minimal time for a down touch action before stating the click animation, this insures that + // there is no click animation on flings + private var mOnDownDelay: Int + private var mTotalClickDelay: Int + + // Minimal distance to move the finger in order to cancel the click animation + private var mMovedPixelToCancel: Float + } + + init { + if (params.containsKey(WEEK_PARAMS_IS_MINI)) { + mIsMiniMonth = params.get(WEEK_PARAMS_IS_MINI) !== 0 + } + mShowAgendaWithMonth = Utils.getConfigBool(context, R.bool.show_agenda_with_month) + val vc: ViewConfiguration = ViewConfiguration.get(context) + mOnDownDelay = ViewConfiguration.getTapTimeout() + mMovedPixelToCancel = vc.getScaledTouchSlop() + mTotalClickDelay = mOnDownDelay + mOnTapDelay + } +} \ No newline at end of file diff --git a/src/com/android/calendar/month/SimpleWeeksAdapter.kt b/src/com/android/calendar/month/SimpleWeeksAdapter.kt index d29b2622..085d2ff4 100644 --- a/src/com/android/calendar/month/SimpleWeeksAdapter.kt +++ b/src/com/android/calendar/month/SimpleWeeksAdapter.kt @@ -13,235 +13,175 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package com.android.calendar.month; - +package com.android.calendar.month // TODO Remove calendar imports when the required methods have been // refactored into the public api -import com.android.calendar.CalendarController; -import com.android.calendar.Utils; - -import android.content.Context; -import android.text.format.Time; -import android.util.Log; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.ViewGroup; -import android.widget.AbsListView.LayoutParams; -import android.widget.BaseAdapter; -import android.widget.ListView; - -import java.util.Calendar; -import java.util.HashMap; -import java.util.Locale; +import com.android.calendar.CalendarController +import com.android.calendar.Utils +import android.content.Context +import android.text.format.Time +import android.util.Log +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.View +import android.view.View.OnTouchListener +import android.view.ViewGroup +import android.widget.AbsListView.LayoutParams +import android.widget.BaseAdapter +import android.widget.ListView +import java.util.Calendar +import java.util.HashMap +import java.util.Locale /** - *

+ * + * * This is a specialized adapter for creating a list of weeks with selectable * days. It can be configured to display the week number, start the week on a * given day, show a reduced number of days, or display an arbitrary number of - * weeks at a time. See {@link SimpleDayPickerFragment} for usage. - *

+ * weeks at a time. See [SimpleDayPickerFragment] for usage. + * */ -public class SimpleWeeksAdapter extends BaseAdapter implements OnTouchListener { - - private static final String TAG = "MonthByWeek"; - - /** - * The number of weeks to display at a time. - */ - public static final String WEEK_PARAMS_NUM_WEEKS = "num_weeks"; - /** - * Which month should be in focus currently. - */ - public static final String WEEK_PARAMS_FOCUS_MONTH = "focus_month"; - /** - * Whether the week number should be shown. Non-zero to show them. - */ - public static final String WEEK_PARAMS_SHOW_WEEK = "week_numbers"; - /** - * Which day the week should start on. {@link Time#SUNDAY} through - * {@link Time#SATURDAY}. - */ - public static final String WEEK_PARAMS_WEEK_START = "week_start"; - /** - * The Julian day to highlight as selected. - */ - public static final String WEEK_PARAMS_JULIAN_DAY = "selected_day"; - /** - * How many days of the week to display [1-7]. - */ - public static final String WEEK_PARAMS_DAYS_PER_WEEK = "days_per_week"; - - protected static final int WEEK_COUNT = CalendarController.MAX_CALENDAR_WEEK - - CalendarController.MIN_CALENDAR_WEEK; - protected static int DEFAULT_NUM_WEEKS = 6; - protected static int DEFAULT_MONTH_FOCUS = 0; - protected static int DEFAULT_DAYS_PER_WEEK = 7; - protected static int DEFAULT_WEEK_HEIGHT = 32; - protected static int WEEK_7_OVERHANG_HEIGHT = 7; +class SimpleWeeksAdapter(context: Context, params: HashMap?) : BaseAdapter(), + OnTouchListener { + protected var mContext: Context - protected static float mScale = 0; - protected Context mContext; // The day to highlight as selected - protected Time mSelectedDay; - // The week since 1970 that the selected day is in - protected int mSelectedWeek; - // When the week starts; numbered like Time. (e.g. SUNDAY=0). - protected int mFirstDayOfWeek; - protected boolean mShowWeekNumber = false; - protected GestureDetector mGestureDetector; - protected int mNumWeeks = DEFAULT_NUM_WEEKS; - protected int mDaysPerWeek = DEFAULT_DAYS_PER_WEEK; - protected int mFocusMonth = DEFAULT_MONTH_FOCUS; + protected var mSelectedDay: Time? = null - public SimpleWeeksAdapter(Context context, HashMap params) { - mContext = context; - - // Get default week start based on locale, subtracting one for use with android Time. - Calendar cal = Calendar.getInstance(Locale.getDefault()); - mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1; + // The week since 1970 that the selected day is in + protected var mSelectedWeek = 0 - if (mScale == 0) { - mScale = context.getResources().getDisplayMetrics().density; - if (mScale != 1) { - WEEK_7_OVERHANG_HEIGHT *= mScale; - } - } - init(); - updateParams(params); - } + // When the week starts; numbered like Time. (e.g. SUNDAY=0). + protected var mFirstDayOfWeek: Int + protected var mShowWeekNumber = false + protected var mGestureDetector: GestureDetector? = null + protected var mNumWeeks = DEFAULT_NUM_WEEKS + protected var mDaysPerWeek = DEFAULT_DAYS_PER_WEEK + protected var mFocusMonth = DEFAULT_MONTH_FOCUS /** * Set up the gesture detector and selected time */ - protected void init() { - mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener()); - mSelectedDay = new Time(); - mSelectedDay.setToNow(); + protected fun init() { + mGestureDetector = GestureDetector(mContext, CalendarGestureListener()) + mSelectedDay = Time() + mSelectedDay.setToNow() } /** * Parse the parameters and set any necessary fields. See - * {@link #WEEK_PARAMS_NUM_WEEKS} for parameter details. + * [.WEEK_PARAMS_NUM_WEEKS] for parameter details. * * @param params A list of parameters for this adapter */ - public void updateParams(HashMap params) { + fun updateParams(params: HashMap?) { if (params == null) { - Log.e(TAG, "WeekParameters are null! Cannot update adapter."); - return; + Log.e(TAG, "WeekParameters are null! Cannot update adapter.") + return } if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) { - mFocusMonth = params.get(WEEK_PARAMS_FOCUS_MONTH); + mFocusMonth = params.get(WEEK_PARAMS_FOCUS_MONTH) } if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) { - mNumWeeks = params.get(WEEK_PARAMS_NUM_WEEKS); + mNumWeeks = params.get(WEEK_PARAMS_NUM_WEEKS) } if (params.containsKey(WEEK_PARAMS_SHOW_WEEK)) { - mShowWeekNumber = params.get(WEEK_PARAMS_SHOW_WEEK) != 0; + mShowWeekNumber = params.get(WEEK_PARAMS_SHOW_WEEK) !== 0 } if (params.containsKey(WEEK_PARAMS_WEEK_START)) { - mFirstDayOfWeek = params.get(WEEK_PARAMS_WEEK_START); + mFirstDayOfWeek = params.get(WEEK_PARAMS_WEEK_START) } if (params.containsKey(WEEK_PARAMS_JULIAN_DAY)) { - int julianDay = params.get(WEEK_PARAMS_JULIAN_DAY); - mSelectedDay.setJulianDay(julianDay); - mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay(julianDay, mFirstDayOfWeek); + val julianDay: Int = params.get(WEEK_PARAMS_JULIAN_DAY) + mSelectedDay.setJulianDay(julianDay) + mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay(julianDay, mFirstDayOfWeek) } if (params.containsKey(WEEK_PARAMS_DAYS_PER_WEEK)) { - mDaysPerWeek = params.get(WEEK_PARAMS_DAYS_PER_WEEK); + mDaysPerWeek = params.get(WEEK_PARAMS_DAYS_PER_WEEK) } - refresh(); + refresh() } - /** - * Updates the selected day and related parameters. + * Returns the currently highlighted day * - * @param selectedTime The time to highlight + * @return */ - public void setSelectedDay(Time selectedTime) { - mSelectedDay.set(selectedTime); - long millis = mSelectedDay.normalize(true); - mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek); - notifyDataSetChanged(); - } - /** - * Returns the currently highlighted day + * Updates the selected day and related parameters. * - * @return + * @param selectedTime The time to highlight */ - public Time getSelectedDay() { - return mSelectedDay; - } + var selectedDay: Time? + get() = mSelectedDay + set(selectedTime) { + mSelectedDay.set(selectedTime) + val millis: Long = mSelectedDay.normalize(true) + mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( + Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek + ) + notifyDataSetChanged() + } /** * updates any config options that may have changed and refreshes the view */ - protected void refresh() { - notifyDataSetChanged(); + protected fun refresh() { + notifyDataSetChanged() } @Override - public int getCount() { - return WEEK_COUNT; + fun getItem(position: Int): Object? { + return null } @Override - public Object getItem(int position) { - return null; - } - - @Override - public long getItemId(int position) { - return position; + fun getItemId(position: Int): Long { + return position.toLong() } @SuppressWarnings("unchecked") @Override - public View getView(int position, View convertView, ViewGroup parent) { - SimpleWeekView v; - HashMap drawingParams = null; + fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val v: SimpleWeekView + var drawingParams: HashMap? = null if (convertView != null) { - v = (SimpleWeekView) convertView; + v = convertView as SimpleWeekView // We store the drawing parameters in the view so it can be recycled - drawingParams = (HashMap) v.getTag(); + drawingParams = v.getTag() as HashMap } else { - v = new SimpleWeekView(mContext); + v = SimpleWeekView(mContext) // Set up the new view - LayoutParams params = new LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - v.setLayoutParams(params); - v.setClickable(true); - v.setOnTouchListener(this); + val params = LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT + ) + v.setLayoutParams(params) + v.setClickable(true) + v.setOnTouchListener(this) } if (drawingParams == null) { - drawingParams = new HashMap(); + drawingParams = HashMap() } - drawingParams.clear(); - - int selectedDay = -1; + drawingParams.clear() + var selectedDay = -1 if (mSelectedWeek == position) { - selectedDay = mSelectedDay.weekDay; + selectedDay = mSelectedDay.weekDay } // pass in all the view parameters - drawingParams.put(SimpleWeekView.VIEW_PARAMS_HEIGHT, - (parent.getHeight() - WEEK_7_OVERHANG_HEIGHT) / mNumWeeks); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, mShowWeekNumber ? 1 : 0); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth); - v.setWeekParams(drawingParams, mSelectedDay.timezone); - v.invalidate(); - - return v; + drawingParams.put( + SimpleWeekView.VIEW_PARAMS_HEIGHT, + (parent.getHeight() - WEEK_7_OVERHANG_HEIGHT) / mNumWeeks + ) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, if (mShowWeekNumber) 1 else 0) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position) + drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth) + v.setWeekParams(drawingParams, mSelectedDay.timezone) + v.invalidate() + return v } /** @@ -249,25 +189,25 @@ public class SimpleWeeksAdapter extends BaseAdapter implements OnTouchListener { * * @param month The month to show as in focus [0-11] */ - public void updateFocusMonth(int month) { - mFocusMonth = month; - notifyDataSetChanged(); + fun updateFocusMonth(month: Int) { + mFocusMonth = month + notifyDataSetChanged() } @Override - public boolean onTouch(View v, MotionEvent event) { + fun onTouch(v: View, event: MotionEvent): Boolean { if (mGestureDetector.onTouchEvent(event)) { - SimpleWeekView view = (SimpleWeekView) v; - Time day = ((SimpleWeekView)v).getDayFromLocation(event.getX()); + val view: SimpleWeekView = v as SimpleWeekView + val day: Time = (v as SimpleWeekView).getDayFromLocation(event.getX()) if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Touched day at Row=" + view.mWeek + " day=" + day.toString()); + Log.d(TAG, "Touched day at Row=" + view.mWeek.toString() + " day=" + day.toString()) } if (day != null) { - onDayTapped(day); + onDayTapped(day) } - return true; + return true } - return false; + return false } /** @@ -275,28 +215,86 @@ public class SimpleWeeksAdapter extends BaseAdapter implements OnTouchListener { * * @param day The day that was tapped */ - protected void onDayTapped(Time day) { - day.hour = mSelectedDay.hour; - day.minute = mSelectedDay.minute; - day.second = mSelectedDay.second; - setSelectedDay(day); + protected fun onDayTapped(day: Time) { + day.hour = mSelectedDay.hour + day.minute = mSelectedDay.minute + day.second = mSelectedDay.second + selectedDay = day } - /** * This is here so we can identify single tap events and set the selected * day correctly */ - protected class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { + protected inner class CalendarGestureListener : GestureDetector.SimpleOnGestureListener() { @Override - public boolean onSingleTapUp(MotionEvent e) { - return true; + fun onSingleTapUp(e: MotionEvent?): Boolean { + return true } } - ListView mListView; + var mListView: ListView? = null + fun setListView(lv: ListView?) { + mListView = lv + } + + companion object { + private const val TAG = "MonthByWeek" + + /** + * The number of weeks to display at a time. + */ + const val WEEK_PARAMS_NUM_WEEKS = "num_weeks" + + /** + * Which month should be in focus currently. + */ + const val WEEK_PARAMS_FOCUS_MONTH = "focus_month" + + /** + * Whether the week number should be shown. Non-zero to show them. + */ + const val WEEK_PARAMS_SHOW_WEEK = "week_numbers" + + /** + * Which day the week should start on. [Time.SUNDAY] through + * [Time.SATURDAY]. + */ + const val WEEK_PARAMS_WEEK_START = "week_start" - public void setListView(ListView lv) { - mListView = lv; + /** + * The Julian day to highlight as selected. + */ + const val WEEK_PARAMS_JULIAN_DAY = "selected_day" + + /** + * How many days of the week to display [1-7]. + */ + const val WEEK_PARAMS_DAYS_PER_WEEK = "days_per_week" + @get:Override val count: Int = (CalendarController.MAX_CALENDAR_WEEK + - CalendarController.MIN_CALENDAR_WEEK) + get() = Companion.field + protected var DEFAULT_NUM_WEEKS = 6 + protected var DEFAULT_MONTH_FOCUS = 0 + protected var DEFAULT_DAYS_PER_WEEK = 7 + protected var DEFAULT_WEEK_HEIGHT = 32 + protected var WEEK_7_OVERHANG_HEIGHT = 7 + protected var mScale = 0f + } + + init { + mContext = context + + // Get default week start based on locale, subtracting one for use with android Time. + val cal: Calendar = Calendar.getInstance(Locale.getDefault()) + mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1 + if (mScale == 0f) { + mScale = context.getResources().getDisplayMetrics().density + if (mScale != 1f) { + WEEK_7_OVERHANG_HEIGHT *= mScale.toInt() + } + } + init() + updateParams(params) } -} +} \ No newline at end of file -- cgit v1.2.3 From 83dac0c872c57ee8ebe066de2e4e0ff808e8aa54 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Fri, 9 Jul 2021 20:21:26 +0000 Subject: AOSP/Calendar - MonthByWeekAdapter and SimpleWeeksAdapter fully converted with bp file This is the fully converted and touched-up Kotlin file after the converter was run once. A few other files had to be tweaked in order ensure all files within the month packages worked together properly. For instance, a few variables in SimpleWeekView.kt had to have their visibility modifier be changed to "internal" rather than "protected". This is because the "protected" modifier in Kotlin means only subclasses have visiblity. Otherwise, it is identical to the "private" modifier. The "internal' modifier more closely resembles "protected" since it is package-private while subclasses still have access to members. I also tried to move away from the "Integer" type and replace it with "Int?" since it is not proper practice according to Kotlin docs to use "Integer" as a type since it is deprecated. Moreover, various functions in SimpleWeeksAdapter.kt had to have the "open" modifier added since these functions were being overridden in MonthByWeekAdapter.kt. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=.......... Time: 0.141 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 7m 48s Total aggregated tests run time: 7m 48s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.80 tests/sec [376 tests / 468191 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 9130 ms || clean = 2088 ms Total preparation time: 9s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 9m 30s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: Ia0fa3cebf53983c3915ed668c9a2f7a570826738 --- Android.bp | 2 + .../android/calendar/month/MonthByWeekAdapter.kt | 139 +++++++++++---------- .../android/calendar/month/MonthByWeekFragment.kt | 32 ++--- .../calendar/month/SimpleDayPickerFragment.kt | 33 +++-- src/com/android/calendar/month/SimpleWeekView.kt | 2 +- .../android/calendar/month/SimpleWeeksAdapter.kt | 112 +++++++++-------- 6 files changed, 174 insertions(+), 146 deletions(-) diff --git a/Android.bp b/Android.bp index fd318cc3..05143be1 100644 --- a/Android.bp +++ b/Android.bp @@ -27,7 +27,9 @@ exclude_srcsd = [ "src/**/calendar/alerts/InitAlarmsService.java", "src/**/calendar/alerts/NotificationMgr.java", "src/**/calendar/alerts/QuickResponseActivity.java", + "src/**/calendar/month/MonthByWeekAdapter.java", "src/**/calendar/month/MonthByWeekFragment.java", + "src/**/calendar/month/SimpleWeeksAdapter.java", "src/**/calendar/widget/CalendarAppWidgetModel.java", "src/**/calendar/widget/CalendarAppWidgetProvider.java", "src/**/calendar/widget/CalendarAppWidgetService.java", diff --git a/src/com/android/calendar/month/MonthByWeekAdapter.kt b/src/com/android/calendar/month/MonthByWeekAdapter.kt index a0ae1b1b..da617950 100644 --- a/src/com/android/calendar/month/MonthByWeekAdapter.kt +++ b/src/com/android/calendar/month/MonthByWeekAdapter.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -37,8 +37,8 @@ import com.android.calendar.Utils import java.util.ArrayList import java.util.HashMap -class MonthByWeekAdapter(context: Context?, params: HashMap) : - SimpleWeeksAdapter(context, params) { +class MonthByWeekAdapter(context: Context?, params: HashMap) : + SimpleWeeksAdapter(context as Context, params) { protected var mController: CalendarController? = null protected var mHomeTimeZone: String? = null protected var mTempTime: Time? = null @@ -56,10 +56,8 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) var mClickedView: MonthWeekEventsView? = null var mSingleTapUpView: MonthWeekEventsView? = null var mLongClickedView: MonthWeekEventsView? = null - var mClickedXLocation // Used to find which day was clicked - = 0f - var mClickTime // Used to calculate minimum click animation time - : Long = 0 + var mClickedXLocation = 0f // Used to find which day was clicked + var mClickTime: Long = 0 // Used to calculate minimum click animation time fun animateToday() { mAnimateToday = true @@ -67,31 +65,31 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) } @Override - protected fun init() { + protected override fun init() { super.init() mGestureDetector = GestureDetector(mContext, CalendarGestureListener()) mController = CalendarController.getInstance(mContext) mHomeTimeZone = Utils.getTimeZone(mContext, null) - mSelectedDay.switchTimezone(mHomeTimeZone) + mSelectedDay?.switchTimezone(mHomeTimeZone) mToday = Time(mHomeTimeZone) - mToday.setToNow() + mToday?.setToNow() mTempTime = Time(mHomeTimeZone) } private fun updateTimeZones() { - mSelectedDay.timezone = mHomeTimeZone - mSelectedDay.normalize(true) - mToday.timezone = mHomeTimeZone - mToday.setToNow() - mTempTime.switchTimezone(mHomeTimeZone) + mSelectedDay!!.timezone = mHomeTimeZone + mSelectedDay?.normalize(true) + mToday!!.timezone = mHomeTimeZone + mToday?.setToNow() + mTempTime?.switchTimezone(mHomeTimeZone) } @Override - fun setSelectedDay(selectedTime: Time?) { - mSelectedDay.set(selectedTime) - val millis: Long = mSelectedDay.normalize(true) + override fun setSelectedDay(selectedTime: Time?) { + mSelectedDay?.set(selectedTime) + val millis: Long = mSelectedDay!!.normalize(true) mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek + Time.getJulianDay(millis, mSelectedDay!!.gmtoff), mFirstDayOfWeek ) notifyDataSetChanged() } @@ -100,8 +98,8 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) if (mIsMiniMonth) { if (Log.isLoggable(TAG, Log.ERROR)) { Log.e( - TAG, "Attempted to set events for mini view. Events only supported in full" - + " view." + TAG, "Attempted to set events for mini view. Events only supported in full" + + " view." ) } return @@ -115,7 +113,7 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) for (i in 0 until numDays) { eventDayList.add(ArrayList()) } - if (events == null || events.size() === 0) { + if (events == null || events.size == 0) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "No events. Returning early--go schedule something fun.") } @@ -147,7 +145,7 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) } } if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Processed " + events.size().toString() + " events.") + Log.d(TAG, "Processed " + events.size.toString() + " events.") } mEventDayList = eventDayList refresh() @@ -155,7 +153,7 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) @SuppressWarnings("unchecked") @Override - fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { if (mIsMiniMonth) { return super.getView(position, convertView, parent) } @@ -163,13 +161,13 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) val params = LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT ) - var drawingParams: HashMap? = null + var drawingParams: HashMap? = null var isAnimatingToday = false if (convertView != null) { v = convertView as MonthWeekEventsView // Checking updateToday uses the current params instead of the new // params, so this is assuming the view is relatively stable - if (mAnimateToday && v.updateToday(mSelectedDay.timezone)) { + if (mAnimateToday && v.updateToday(mSelectedDay!!.timezone)) { val currentTime: Long = System.currentTimeMillis() // If it's been too long since we tried to start the animation // don't show it. This can happen if the user stops a scroll @@ -184,13 +182,13 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) v = MonthWeekEventsView(mContext) } } else { - drawingParams = v.getTag() as HashMap + drawingParams = v.getTag() as HashMap } } else { v = MonthWeekEventsView(mContext) } if (drawingParams == null) { - drawingParams = HashMap() + drawingParams = HashMap() } drawingParams.clear() v.setLayoutParams(params) @@ -198,7 +196,7 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) v.setOnTouchListener(this) var selectedDay = -1 if (mSelectedWeek === position) { - selectedDay = mSelectedDay.weekDay + selectedDay = mSelectedDay!!.weekDay } drawingParams.put( SimpleWeekView.VIEW_PARAMS_HEIGHT, @@ -215,12 +213,12 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ANIMATE_TODAY, 1) mAnimateToday = false } - v.setWeekParams(drawingParams, mSelectedDay.timezone) + v.setWeekParams(drawingParams, mSelectedDay!!.timezone) return v } @Override - protected fun refresh() { + internal override fun refresh() { mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext) mShowWeekNumber = Utils.getShowWeekNumber(mContext) mHomeTimeZone = Utils.getTimeZone(mContext, null) @@ -230,21 +228,21 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) } @Override - protected fun onDayTapped(day: Time) { + protected override fun onDayTapped(day: Time) { setDayParameters(day) if (mShowAgendaWithMonth || mIsMiniMonth) { // If agenda view is visible with month view , refresh the views // with the selected day's info - mController.sendEvent( - mContext, EventType.GO_TO, day, day, -1, + mController?.sendEvent( + mContext as Object?, EventType.GO_TO, day, day, -1, ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null ) } else { // Else , switch to the detailed view - mController.sendEvent( - mContext, EventType.GO_TO, day, day, -1, + mController?.sendEvent( + mContext as Object?, EventType.GO_TO, day, day, -1, ViewType.DETAIL, CalendarController.EXTRA_GOTO_DATE - or CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS, null, null + or CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS, null, null ) } } @@ -252,7 +250,7 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) private fun setDayParameters(day: Time) { day.timezone = mHomeTimeZone val currTime = Time(mHomeTimeZone) - currTime.set(mController.getTime()) + currTime.set(mController!!.time as Long) day.hour = currTime.hour day.minute = currTime.minute day.allDay = false @@ -260,19 +258,19 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) } @Override - fun onTouch(v: View?, event: MotionEvent): Boolean { + override fun onTouch(v: View, event: MotionEvent): Boolean { if (v !is MonthWeekEventsView) { return super.onTouch(v, event) } - val action: Int = event.getAction() + val action: Int = event!!.getAction() // Event was tapped - switch to the detailed view making sure the click animation // is done first. - if (mGestureDetector.onTouchEvent(event)) { + if (mGestureDetector!!.onTouchEvent(event)) { mSingleTapUpView = v as MonthWeekEventsView? val delay: Long = System.currentTimeMillis() - mClickTime // Make sure the animation is visible for at least mOnTapDelay - mOnDownDelay ms - mListView.postDelayed( + mListView?.postDelayed( mDoSingleTapUp, if (delay > mTotalClickDelay) 0 else mTotalClickDelay - delay ) @@ -285,12 +283,14 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) mClickedView = v as MonthWeekEventsView mClickedXLocation = event.getX() mClickTime = System.currentTimeMillis() - mListView.postDelayed(mDoClick, mOnDownDelay) + mListView?.postDelayed(mDoClick, mOnDownDelay.toLong()) } - MotionEvent.ACTION_UP, MotionEvent.ACTION_SCROLL, MotionEvent.ACTION_CANCEL -> clearClickedView( + MotionEvent.ACTION_UP, MotionEvent.ACTION_SCROLL, MotionEvent.ACTION_CANCEL -> + clearClickedView( v as MonthWeekEventsView? ) - MotionEvent.ACTION_MOVE -> // No need to cancel on vertical movement, ACTION_SCROLL will do that. + MotionEvent.ACTION_MOVE -> // No need to cancel on vertical movement, + // ACTION_SCROLL will do that. if (Math.abs(event.getX() - mClickedXLocation) > mMovedPixelToCancel) { clearClickedView(v as MonthWeekEventsView?) } @@ -308,20 +308,20 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) */ protected inner class CalendarGestureListener : GestureDetector.SimpleOnGestureListener() { @Override - fun onSingleTapUp(e: MotionEvent?): Boolean { + override fun onSingleTapUp(e: MotionEvent?): Boolean { return true } @Override - fun onLongPress(e: MotionEvent?) { + override fun onLongPress(e: MotionEvent?) { if (mLongClickedView != null) { - val day: Time = mLongClickedView.getDayFromLocation(mClickedXLocation) + val day: Time? = mLongClickedView?.getDayFromLocation(mClickedXLocation) if (day != null) { - mLongClickedView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) + mLongClickedView?.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) val message = Message() message.obj = day } - mLongClickedView.clearClickedDay() + mLongClickedView?.clearClickedDay() mLongClickedView = null } } @@ -329,38 +329,40 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) // Clear the visual cues of the click animation and related running code. private fun clearClickedView(v: MonthWeekEventsView?) { - mListView.removeCallbacks(mDoClick) - synchronized(v) { v.clearClickedDay() } + mListView?.removeCallbacks(mDoClick) + synchronized(v as Any) { v?.clearClickedDay() } mClickedView = null } // Perform the tap animation in a runnable to allow a delay before showing the tap color. // This is done to prevent a click animation when a fling is done. - private val mDoClick: Runnable = object : Runnable() { + private val mDoClick: Runnable = object : Runnable { @Override - fun run() { + override fun run() { if (mClickedView != null) { - synchronized(mClickedView) { mClickedView.setClickedDay(mClickedXLocation) } + synchronized(mClickedView as MonthWeekEventsView) { + mClickedView?.setClickedDay(mClickedXLocation) } mLongClickedView = mClickedView mClickedView = null // This is a workaround , sometimes the top item on the listview doesn't refresh on // invalidate, so this forces a re-draw. - mListView.invalidate() + mListView?.invalidate() } } } // Performs the single tap operation: go to the tapped day. // This is done in a runnable to allow the click animation to finish before switching views - private val mDoSingleTapUp: Runnable = object : Runnable() { + private val mDoSingleTapUp: Runnable = object : Runnable { @Override - fun run() { + override fun run() { if (mSingleTapUpView != null) { - val day: Time = mSingleTapUpView.getDayFromLocation(mClickedXLocation) + val day: Time? = mSingleTapUpView?.getDayFromLocation(mClickedXLocation) if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d( TAG, - "Touched day at Row=" + mSingleTapUpView.mWeek.toString() + " day=" + day.toString() + "Touched day at Row=" + mSingleTapUpView?.mWeek?.toString() + + " day=" + day?.toString() ) } if (day != null) { @@ -381,23 +383,24 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) // Used to insure minimal time for seeing the click animation before switching views private const val mOnTapDelay = 100 - // Minimal time for a down touch action before stating the click animation, this insures that - // there is no click animation on flings - private var mOnDownDelay: Int - private var mTotalClickDelay: Int + // Minimal time for a down touch action before stating the click animation, this ensures + // that there is no click animation on flings + private var mOnDownDelay: Int = 0 + private var mTotalClickDelay: Int = 0 // Minimal distance to move the finger in order to cancel the click animation - private var mMovedPixelToCancel: Float + private var mMovedPixelToCancel: Float = 0f } init { if (params.containsKey(WEEK_PARAMS_IS_MINI)) { - mIsMiniMonth = params.get(WEEK_PARAMS_IS_MINI) !== 0 + mIsMiniMonth = params.get(WEEK_PARAMS_IS_MINI) != 0 } - mShowAgendaWithMonth = Utils.getConfigBool(context, R.bool.show_agenda_with_month) + mShowAgendaWithMonth = Utils.getConfigBool(context as Context, + R.bool.show_agenda_with_month) val vc: ViewConfiguration = ViewConfiguration.get(context) mOnDownDelay = ViewConfiguration.getTapTimeout() - mMovedPixelToCancel = vc.getScaledTouchSlop() + mMovedPixelToCancel = vc.getScaledTouchSlop().toFloat() mTotalClickDelay = mOnDownDelay + mOnTapDelay } } \ No newline at end of file diff --git a/src/com/android/calendar/month/MonthByWeekFragment.kt b/src/com/android/calendar/month/MonthByWeekFragment.kt index 7c1eec9a..9fe9fe49 100644 --- a/src/com/android/calendar/month/MonthByWeekFragment.kt +++ b/src/com/android/calendar/month/MonthByWeekFragment.kt @@ -53,8 +53,8 @@ import java.util.Calendar import java.util.HashMap class MonthByWeekFragment @JvmOverloads constructor( - initialTime: Long = System.currentTimeMillis(), - protected var mIsMiniMonth: Boolean = true + initialTime: Long = System.currentTimeMillis(), + protected var mIsMiniMonth: Boolean = true ) : SimpleDayPickerFragment(initialTime), CalendarController.EventHandler, LoaderManager.LoaderCallbacks, OnScrollListener, OnTouchListener { protected var mMinimumTwoMonthFlingVelocity = 0f @@ -221,18 +221,18 @@ class MonthByWeekFragment @JvmOverloads constructor( protected override fun setUpAdapter() { mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext) mShowWeekNumber = Utils.getShowWeekNumber(mContext) - val weekParams: HashMap = HashMap() - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks) - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, if (mShowWeekNumber) 1 else 0) - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek) - weekParams.put(MonthByWeekAdapter.WEEK_PARAMS_IS_MINI, if (mIsMiniMonth) 1 else 0) - weekParams.put( + val weekParams = HashMap() + weekParams?.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks) + weekParams?.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, if (mShowWeekNumber) 1 else 0) + weekParams?.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek) + weekParams?.put(MonthByWeekAdapter.WEEK_PARAMS_IS_MINI, if (mIsMiniMonth) 1 else 0) + weekParams?.put( SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) ) weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_DAYS_PER_WEEK, mDaysPerWeek) if (mAdapter == null) { - mAdapter = MonthByWeekAdapter(getActivity(), weekParams) + mAdapter = MonthByWeekAdapter(getActivity(), weekParams) as SimpleWeeksAdapter? mAdapter?.registerDataSetObserver(mObserver) } else { mAdapter?.updateParams(weekParams) @@ -242,9 +242,9 @@ class MonthByWeekFragment @JvmOverloads constructor( @Override override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? ): View { val v: View v = if (mIsMiniMonth) { @@ -338,7 +338,8 @@ class MonthByWeekFragment @JvmOverloads constructor( if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d( TAG, - "Found " + data?.getCount()?.toString() + " cursor entries for uri " + mEventUri + "Found " + data?.getCount()?.toString() + " cursor entries for uri " + + mEventUri ) } val cLoader: CursorLoader = loader as CursorLoader @@ -357,7 +358,7 @@ class MonthByWeekFragment @JvmOverloads constructor( ) (mAdapter as MonthByWeekAdapter).setEvents( mFirstLoadedJulianDay, - mLastLoadedJulianDay - mFirstLoadedJulianDay + 1, events + mLastLoadedJulianDay - mFirstLoadedJulianDay + 1, events as ArrayList? ) } } @@ -396,7 +397,8 @@ class MonthByWeekFragment @JvmOverloads constructor( val animateToday = event?.extraLong and CalendarController.EXTRA_GOTO_TODAY.toLong() != 0L val delayAnimation: Boolean = - goTo(event?.selectedTime?.toMillis(true)?.toLong() as Long, animate, true, false) + goTo(event?.selectedTime?.toMillis(true)?.toLong() as Long, + animate, true, false) if (animateToday) { // If we need to flash today start the animation after any // movement from listView has ended. diff --git a/src/com/android/calendar/month/SimpleDayPickerFragment.kt b/src/com/android/calendar/month/SimpleDayPickerFragment.kt index d3c43e66..01fcbac6 100644 --- a/src/com/android/calendar/month/SimpleDayPickerFragment.kt +++ b/src/com/android/calendar/month/SimpleDayPickerFragment.kt @@ -132,9 +132,9 @@ open class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScroll @JvmField protected var mObserver: DataSetObserver = object : DataSetObserver() { @Override override fun onChanged() { - val day: Time = mAdapter!!.getSelectedDay() - if (day.year !== mSelectedDay.year || day.yearDay !== mSelectedDay.yearDay) { - goTo(day.toMillis(true), true, true, false) + val day: Time? = mAdapter!!.getSelectedDay() + if (day!!.year !== mSelectedDay!!.year || day!!.yearDay !== mSelectedDay.yearDay) { + goTo(day!!.toMillis(true), true, true, false) } } } @@ -178,11 +178,11 @@ open class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScroll * this method to provide a custom adapter. */ protected open fun setUpAdapter() { - val weekParams: HashMap = HashMap() - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks) - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, if (mShowWeekNumber) 1 else 0) - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek) - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, + val weekParams = HashMap() + weekParams?.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks) + weekParams?.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, if (mShowWeekNumber) 1 else 0) + weekParams?.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek) + weekParams?.put(SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, Time.getJulianDay(mSelectedDay.toMillis(false), mSelectedDay.gmtoff)) if (mAdapter == null) { mAdapter = SimpleWeeksAdapter(getActivity(), weekParams) @@ -316,8 +316,11 @@ open class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScroll } @Override - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { val v: View = inflater.inflate(R.layout.month_by_week, container, false) mDayNamesHeader = v.findViewById(R.id.day_names) as ViewGroup @@ -438,7 +441,11 @@ open class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScroll */ @Override override fun onScroll( - view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) { + view: AbsListView, + firstVisibleItem: Int, + visibleItemCount: Int, + totalItemCount: Int + ) { val child = view.getChildAt(0) as? SimpleWeekView if (child == null) { return @@ -569,8 +576,8 @@ open class SimpleDayPickerFragment(initialTime: Long) : ListFragment(), OnScroll "new scroll state: $mNewState old state: $mPreviousScrollState") } // Fix the position after a scroll or a fling ends - if (mNewState == OnScrollListener.SCROLL_STATE_IDLE - && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) { + if (mNewState == OnScrollListener.SCROLL_STATE_IDLE && + mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) { mPreviousScrollState = mNewState mAdapter?.updateFocusMonth(mCurrentMonthDisplayed) } else { diff --git a/src/com/android/calendar/month/SimpleWeekView.kt b/src/com/android/calendar/month/SimpleWeekView.kt index 7c1ef44e..a71cff86 100644 --- a/src/com/android/calendar/month/SimpleWeekView.kt +++ b/src/com/android/calendar/month/SimpleWeekView.kt @@ -72,7 +72,7 @@ open class SimpleWeekView(context: Context) : View(context) { // The position of this week, equivalent to weeks since the week of Jan 1st, // 1970 - @JvmField protected var mWeek = -1 + @JvmField var mWeek = -1 // Quick reference to the width of this view, matches parent @JvmField protected var mWidth = 0 diff --git a/src/com/android/calendar/month/SimpleWeeksAdapter.kt b/src/com/android/calendar/month/SimpleWeeksAdapter.kt index 085d2ff4..67be4d89 100644 --- a/src/com/android/calendar/month/SimpleWeeksAdapter.kt +++ b/src/com/android/calendar/month/SimpleWeeksAdapter.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -42,8 +42,8 @@ import java.util.Locale * weeks at a time. See [SimpleDayPickerFragment] for usage. * */ -class SimpleWeeksAdapter(context: Context, params: HashMap?) : BaseAdapter(), - OnTouchListener { +open class SimpleWeeksAdapter(context: Context, params: HashMap?) : BaseAdapter(), + OnTouchListener { protected var mContext: Context // The day to highlight as selected @@ -63,10 +63,10 @@ class SimpleWeeksAdapter(context: Context, params: HashMap?) /** * Set up the gesture detector and selected time */ - protected fun init() { + protected open fun init() { mGestureDetector = GestureDetector(mContext, CalendarGestureListener()) mSelectedDay = Time() - mSelectedDay.setToNow() + mSelectedDay?.setToNow() } /** @@ -75,80 +75,94 @@ class SimpleWeeksAdapter(context: Context, params: HashMap?) * * @param params A list of parameters for this adapter */ - fun updateParams(params: HashMap?) { + fun updateParams(params: HashMap?) { if (params == null) { Log.e(TAG, "WeekParameters are null! Cannot update adapter.") return } if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) { - mFocusMonth = params.get(WEEK_PARAMS_FOCUS_MONTH) + // Casting from Int? --> Int + mFocusMonth = params.get(WEEK_PARAMS_FOCUS_MONTH) as Int } if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) { - mNumWeeks = params.get(WEEK_PARAMS_NUM_WEEKS) + // Casting from Int? --> Int + mNumWeeks = params.get(WEEK_PARAMS_NUM_WEEKS) as Int } if (params.containsKey(WEEK_PARAMS_SHOW_WEEK)) { - mShowWeekNumber = params.get(WEEK_PARAMS_SHOW_WEEK) !== 0 + // Casting from Int? --> Int + mShowWeekNumber = params.get(WEEK_PARAMS_SHOW_WEEK) as Int != 0 } if (params.containsKey(WEEK_PARAMS_WEEK_START)) { - mFirstDayOfWeek = params.get(WEEK_PARAMS_WEEK_START) + // Casting from Int? --> Int + mFirstDayOfWeek = params.get(WEEK_PARAMS_WEEK_START) as Int } if (params.containsKey(WEEK_PARAMS_JULIAN_DAY)) { - val julianDay: Int = params.get(WEEK_PARAMS_JULIAN_DAY) - mSelectedDay.setJulianDay(julianDay) + // Casting from Int? --> Int + val julianDay: Int = params.get(WEEK_PARAMS_JULIAN_DAY) as Int + mSelectedDay?.setJulianDay(julianDay) mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay(julianDay, mFirstDayOfWeek) } if (params.containsKey(WEEK_PARAMS_DAYS_PER_WEEK)) { - mDaysPerWeek = params.get(WEEK_PARAMS_DAYS_PER_WEEK) + // Casting from Int? --> Int + mDaysPerWeek = params.get(WEEK_PARAMS_DAYS_PER_WEEK) as Int } refresh() } + /** - * Returns the currently highlighted day + * Updates the selected day and related parameters. * - * @return + * @param selectedTime The time to highlight */ + open fun setSelectedDay(selectedTime: Time?) { + mSelectedDay?.set(selectedTime) + val millis: Long = mSelectedDay!!.normalize(true) + mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( + Time.getJulianDay(millis, mSelectedDay!!.gmtoff), mFirstDayOfWeek + ) + notifyDataSetChanged() + } + /** - * Updates the selected day and related parameters. + * Returns the currently highlighted day * - * @param selectedTime The time to highlight + * @return */ - var selectedDay: Time? - get() = mSelectedDay - set(selectedTime) { - mSelectedDay.set(selectedTime) - val millis: Long = mSelectedDay.normalize(true) - mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek - ) - notifyDataSetChanged() - } + fun getSelectedDay(): Time? { + return mSelectedDay + } /** * updates any config options that may have changed and refreshes the view */ - protected fun refresh() { + internal open fun refresh() { notifyDataSetChanged() } @Override - fun getItem(position: Int): Object? { + override fun getCount(): Int { + return WEEK_COUNT + } + + @Override + override fun getItem(position: Int): Any? { return null } @Override - fun getItemId(position: Int): Long { + override fun getItemId(position: Int): Long { return position.toLong() } @SuppressWarnings("unchecked") @Override - fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val v: SimpleWeekView - var drawingParams: HashMap? = null + var drawingParams: HashMap? = null if (convertView != null) { v = convertView as SimpleWeekView // We store the drawing parameters in the view so it can be recycled - drawingParams = v.getTag() as HashMap + drawingParams = v.getTag() as HashMap } else { v = SimpleWeekView(mContext) // Set up the new view @@ -160,12 +174,12 @@ class SimpleWeeksAdapter(context: Context, params: HashMap?) v.setOnTouchListener(this) } if (drawingParams == null) { - drawingParams = HashMap() + drawingParams = HashMap() } drawingParams.clear() var selectedDay = -1 if (mSelectedWeek == position) { - selectedDay = mSelectedDay.weekDay + selectedDay = mSelectedDay!!.weekDay } // pass in all the view parameters @@ -179,7 +193,7 @@ class SimpleWeeksAdapter(context: Context, params: HashMap?) drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek) drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position) drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth) - v.setWeekParams(drawingParams, mSelectedDay.timezone) + v.setWeekParams(drawingParams, mSelectedDay!!.timezone) v.invalidate() return v } @@ -195,12 +209,13 @@ class SimpleWeeksAdapter(context: Context, params: HashMap?) } @Override - fun onTouch(v: View, event: MotionEvent): Boolean { - if (mGestureDetector.onTouchEvent(event)) { + override fun onTouch(v: View, event: MotionEvent): Boolean { + if (mGestureDetector!!.onTouchEvent(event)) { val view: SimpleWeekView = v as SimpleWeekView - val day: Time = (v as SimpleWeekView).getDayFromLocation(event.getX()) + val day: Time? = (v as SimpleWeekView).getDayFromLocation(event.getX()) if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Touched day at Row=" + view.mWeek.toString() + " day=" + day.toString()) + Log.d(TAG, "Touched day at Row=" + view.mWeek.toString() + " day=" + + day?.toString()) } if (day != null) { onDayTapped(day) @@ -215,11 +230,11 @@ class SimpleWeeksAdapter(context: Context, params: HashMap?) * * @param day The day that was tapped */ - protected fun onDayTapped(day: Time) { - day.hour = mSelectedDay.hour - day.minute = mSelectedDay.minute - day.second = mSelectedDay.second - selectedDay = day + protected open fun onDayTapped(day: Time) { + day.hour = mSelectedDay!!.hour + day.minute = mSelectedDay!!.minute + day.second = mSelectedDay!!.second + setSelectedDay(day) } /** @@ -228,7 +243,7 @@ class SimpleWeeksAdapter(context: Context, params: HashMap?) */ protected inner class CalendarGestureListener : GestureDetector.SimpleOnGestureListener() { @Override - fun onSingleTapUp(e: MotionEvent?): Boolean { + override fun onSingleTapUp(e: MotionEvent?): Boolean { return true } } @@ -271,9 +286,8 @@ class SimpleWeeksAdapter(context: Context, params: HashMap?) * How many days of the week to display [1-7]. */ const val WEEK_PARAMS_DAYS_PER_WEEK = "days_per_week" - @get:Override val count: Int = (CalendarController.MAX_CALENDAR_WEEK - - CalendarController.MIN_CALENDAR_WEEK) - get() = Companion.field + protected const val WEEK_COUNT = CalendarController.MAX_CALENDAR_WEEK - + CalendarController.MIN_CALENDAR_WEEK protected var DEFAULT_NUM_WEEKS = 6 protected var DEFAULT_MONTH_FOCUS = 0 protected var DEFAULT_DAYS_PER_WEEK = 7 -- cgit v1.2.3 From 7adab399715fcad7ea185eb9e349c9a073d3a0f3 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Mon, 12 Jul 2021 18:57:04 +0000 Subject: AOSP/Calendar - Add Kotlin copy of MonthWeekEventsView.java Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: I4d956b5f0ae5f983ac30d09e01b55ff3a75bd606 --- .../android/calendar/month/MonthWeekEventsView.kt | 1110 ++++++++++++++++++++ 1 file changed, 1110 insertions(+) create mode 100644 src/com/android/calendar/month/MonthWeekEventsView.kt diff --git a/src/com/android/calendar/month/MonthWeekEventsView.kt b/src/com/android/calendar/month/MonthWeekEventsView.kt new file mode 100644 index 00000000..071daee3 --- /dev/null +++ b/src/com/android/calendar/month/MonthWeekEventsView.kt @@ -0,0 +1,1110 @@ +/* + * Copyright (C) 2010 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.month; + +import com.android.calendar.Event; +import com.android.calendar.R; +import com.android.calendar.Utils; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.app.Service; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.Style; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.provider.CalendarContract.Attendees; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.text.format.DateUtils; +import android.text.format.Time; +import android.util.Log; +import android.view.MotionEvent; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Formatter; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + +public class MonthWeekEventsView extends SimpleWeekView { + + private static final String TAG = "MonthView"; + + private static final boolean DEBUG_LAYOUT = false; + + public static final String VIEW_PARAMS_ORIENTATION = "orientation"; + public static final String VIEW_PARAMS_ANIMATE_TODAY = "animate_today"; + + /* NOTE: these are not constants, and may be multiplied by a scale factor */ + private static int TEXT_SIZE_MONTH_NUMBER = 32; + private static int TEXT_SIZE_EVENT = 12; + private static int TEXT_SIZE_EVENT_TITLE = 14; + private static int TEXT_SIZE_MORE_EVENTS = 12; + private static int TEXT_SIZE_MONTH_NAME = 14; + private static int TEXT_SIZE_WEEK_NUM = 12; + + private static int DNA_MARGIN = 4; + private static int DNA_ALL_DAY_HEIGHT = 4; + private static int DNA_MIN_SEGMENT_HEIGHT = 4; + private static int DNA_WIDTH = 8; + private static int DNA_ALL_DAY_WIDTH = 32; + private static int DNA_SIDE_PADDING = 6; + private static int CONFLICT_COLOR = Color.BLACK; + private static int EVENT_TEXT_COLOR = Color.WHITE; + + private static int DEFAULT_EDGE_SPACING = 0; + private static int SIDE_PADDING_MONTH_NUMBER = 4; + private static int TOP_PADDING_MONTH_NUMBER = 4; + private static int TOP_PADDING_WEEK_NUMBER = 4; + private static int SIDE_PADDING_WEEK_NUMBER = 20; + private static int DAY_SEPARATOR_OUTER_WIDTH = 0; + private static int DAY_SEPARATOR_INNER_WIDTH = 1; + private static int DAY_SEPARATOR_VERTICAL_LENGTH = 53; + private static int DAY_SEPARATOR_VERTICAL_LENGTH_PORTRAIT = 64; + private static int MIN_WEEK_WIDTH = 50; + + private static int EVENT_X_OFFSET_LANDSCAPE = 38; + private static int EVENT_Y_OFFSET_LANDSCAPE = 8; + private static int EVENT_Y_OFFSET_PORTRAIT = 7; + private static int EVENT_SQUARE_WIDTH = 10; + private static int EVENT_SQUARE_BORDER = 2; + private static int EVENT_LINE_PADDING = 2; + private static int EVENT_RIGHT_PADDING = 4; + private static int EVENT_BOTTOM_PADDING = 3; + + private static int TODAY_HIGHLIGHT_WIDTH = 2; + + private static int SPACING_WEEK_NUMBER = 24; + private static boolean mInitialized = false; + private static boolean mShowDetailsInMonth; + + protected Time mToday = new Time(); + protected boolean mHasToday = false; + protected int mTodayIndex = -1; + protected int mOrientation = Configuration.ORIENTATION_LANDSCAPE; + protected List> mEvents = null; + protected ArrayList mUnsortedEvents = null; + HashMap mDna = null; + // This is for drawing the outlines around event chips and supports up to 10 + // events being drawn on each day. The code will expand this if necessary. + protected FloatRef mEventOutlines = new FloatRef(10 * 4 * 4 * 7); + + + + protected static StringBuilder mStringBuilder = new StringBuilder(50); + // TODO recreate formatter when locale changes + protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); + + protected Paint mMonthNamePaint; + protected TextPaint mEventPaint; + protected TextPaint mSolidBackgroundEventPaint; + protected TextPaint mFramedEventPaint; + protected TextPaint mDeclinedEventPaint; + protected TextPaint mEventExtrasPaint; + protected TextPaint mEventDeclinedExtrasPaint; + protected Paint mWeekNumPaint; + protected Paint mDNAAllDayPaint; + protected Paint mDNATimePaint; + protected Paint mEventSquarePaint; + + + protected Drawable mTodayDrawable; + + protected int mMonthNumHeight; + protected int mMonthNumAscentHeight; + protected int mEventHeight; + protected int mEventAscentHeight; + protected int mExtrasHeight; + protected int mExtrasAscentHeight; + protected int mExtrasDescent; + protected int mWeekNumAscentHeight; + + protected int mMonthBGColor; + protected int mMonthBGOtherColor; + protected int mMonthBGTodayColor; + protected int mMonthNumColor; + protected int mMonthNumOtherColor; + protected int mMonthNumTodayColor; + protected int mMonthNameColor; + protected int mMonthNameOtherColor; + protected int mMonthEventColor; + protected int mMonthDeclinedEventColor; + protected int mMonthDeclinedExtrasColor; + protected int mMonthEventExtraColor; + protected int mMonthEventOtherColor; + protected int mMonthEventExtraOtherColor; + protected int mMonthWeekNumColor; + protected int mMonthBusyBitsBgColor; + protected int mMonthBusyBitsBusyTimeColor; + protected int mMonthBusyBitsConflictTimeColor; + private int mClickedDayIndex = -1; + private int mClickedDayColor; + private static final int mClickedAlpha = 128; + + protected int mEventChipOutlineColor = 0xFFFFFFFF; + protected int mDaySeparatorInnerColor; + protected int mTodayAnimateColor; + + private boolean mAnimateToday; + private int mAnimateTodayAlpha = 0; + private ObjectAnimator mTodayAnimator = null; + + private final TodayAnimatorListener mAnimatorListener = new TodayAnimatorListener(); + + class TodayAnimatorListener extends AnimatorListenerAdapter { + private volatile Animator mAnimator = null; + private volatile boolean mFadingIn = false; + + @Override + public void onAnimationEnd(Animator animation) { + synchronized (this) { + if (mAnimator != animation) { + animation.removeAllListeners(); + animation.cancel(); + return; + } + if (mFadingIn) { + if (mTodayAnimator != null) { + mTodayAnimator.removeAllListeners(); + mTodayAnimator.cancel(); + } + mTodayAnimator = ObjectAnimator.ofInt(MonthWeekEventsView.this, + "animateTodayAlpha", 255, 0); + mAnimator = mTodayAnimator; + mFadingIn = false; + mTodayAnimator.addListener(this); + mTodayAnimator.setDuration(600); + mTodayAnimator.start(); + } else { + mAnimateToday = false; + mAnimateTodayAlpha = 0; + mAnimator.removeAllListeners(); + mAnimator = null; + mTodayAnimator = null; + invalidate(); + } + } + } + + public void setAnimator(Animator animation) { + mAnimator = animation; + } + + public void setFadingIn(boolean fadingIn) { + mFadingIn = fadingIn; + } + + } + + private int[] mDayXs; + + /** + * This provides a reference to a float array which allows for easy size + * checking and reallocation. Used for drawing lines. + */ + private class FloatRef { + float[] array; + + public FloatRef(int size) { + array = new float[size]; + } + + public void ensureSize(int newSize) { + if (newSize >= array.length) { + // Add enough space for 7 more boxes to be drawn + array = Arrays.copyOf(array, newSize + 16 * 7); + } + } + } + + /** + * Shows up as an error if we don't include this. + */ + public MonthWeekEventsView(Context context) { + super(context); + } + + // Sets the list of events for this week. Takes a sorted list of arrays + // divided up by day for generating the large month version and the full + // arraylist sorted by start time to generate the dna version. + public void setEvents(List> sortedEvents, ArrayList unsortedEvents) { + setEvents(sortedEvents); + // The MIN_WEEK_WIDTH is a hack to prevent the view from trying to + // generate dna bits before its width has been fixed. + createDna(unsortedEvents); + } + + /** + * Sets up the dna bits for the view. This will return early if the view + * isn't in a state that will create a valid set of dna yet (such as the + * views width not being set correctly yet). + */ + public void createDna(ArrayList unsortedEvents) { + if (unsortedEvents == null || mWidth <= MIN_WEEK_WIDTH || getContext() == null) { + // Stash the list of events for use when this view is ready, or + // just clear it if a null set has been passed to this view + mUnsortedEvents = unsortedEvents; + mDna = null; + return; + } else { + // clear the cached set of events since we're ready to build it now + mUnsortedEvents = null; + } + // Create the drawing coordinates for dna + if (!mShowDetailsInMonth) { + int numDays = mEvents.size(); + int effectiveWidth = mWidth - mPadding * 2; + if (mShowWeekNum) { + effectiveWidth -= SPACING_WEEK_NUMBER; + } + DNA_ALL_DAY_WIDTH = effectiveWidth / numDays - 2 * DNA_SIDE_PADDING; + mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH); + mDayXs = new int[numDays]; + for (int day = 0; day < numDays; day++) { + mDayXs[day] = computeDayLeftPosition(day) + DNA_WIDTH / 2 + DNA_SIDE_PADDING; + + } + + int top = DAY_SEPARATOR_INNER_WIDTH + DNA_MARGIN + DNA_ALL_DAY_HEIGHT + 1; + int bottom = mHeight - DNA_MARGIN; + mDna = Utils.createDNAStrands(mFirstJulianDay, unsortedEvents, top, bottom, + DNA_MIN_SEGMENT_HEIGHT, mDayXs, getContext()); + } + } + + public void setEvents(List> sortedEvents) { + mEvents = sortedEvents; + if (sortedEvents == null) { + return; + } + if (sortedEvents.size() != mNumDays) { + if (Log.isLoggable(TAG, Log.ERROR)) { + Log.wtf(TAG, "Events size must be same as days displayed: size=" + + sortedEvents.size() + " days=" + mNumDays); + } + mEvents = null; + return; + } + } + + protected void loadColors(Context context) { + Resources res = context.getResources(); + mMonthWeekNumColor = res.getColor(R.color.month_week_num_color); + mMonthNumColor = res.getColor(R.color.month_day_number); + mMonthNumOtherColor = res.getColor(R.color.month_day_number_other); + mMonthNumTodayColor = res.getColor(R.color.month_today_number); + mMonthNameColor = mMonthNumColor; + mMonthNameOtherColor = mMonthNumOtherColor; + mMonthEventColor = res.getColor(R.color.month_event_color); + mMonthDeclinedEventColor = res.getColor(R.color.agenda_item_declined_color); + mMonthDeclinedExtrasColor = res.getColor(R.color.agenda_item_where_declined_text_color); + mMonthEventExtraColor = res.getColor(R.color.month_event_extra_color); + mMonthEventOtherColor = res.getColor(R.color.month_event_other_color); + mMonthEventExtraOtherColor = res.getColor(R.color.month_event_extra_other_color); + mMonthBGTodayColor = res.getColor(R.color.month_today_bgcolor); + mMonthBGOtherColor = res.getColor(R.color.month_other_bgcolor); + mMonthBGColor = res.getColor(R.color.month_bgcolor); + mDaySeparatorInnerColor = res.getColor(R.color.month_grid_lines); + mTodayAnimateColor = res.getColor(R.color.today_highlight_color); + mClickedDayColor = res.getColor(R.color.day_clicked_background_color); + mTodayDrawable = res.getDrawable(R.drawable.today_blue_week_holo_light); + } + + /** + * Sets up the text and style properties for painting. Override this if you + * want to use a different paint. + */ + @Override + protected void initView() { + super.initView(); + + if (!mInitialized) { + Resources resources = getContext().getResources(); + mShowDetailsInMonth = Utils.getConfigBool(getContext(), R.bool.show_details_in_month); + TEXT_SIZE_EVENT_TITLE = resources.getInteger(R.integer.text_size_event_title); + TEXT_SIZE_MONTH_NUMBER = resources.getInteger(R.integer.text_size_month_number); + SIDE_PADDING_MONTH_NUMBER = resources.getInteger(R.integer.month_day_number_margin); + CONFLICT_COLOR = resources.getColor(R.color.month_dna_conflict_time_color); + EVENT_TEXT_COLOR = resources.getColor(R.color.calendar_event_text_color); + if (mScale != 1) { + TOP_PADDING_MONTH_NUMBER *= mScale; + TOP_PADDING_WEEK_NUMBER *= mScale; + SIDE_PADDING_MONTH_NUMBER *= mScale; + SIDE_PADDING_WEEK_NUMBER *= mScale; + SPACING_WEEK_NUMBER *= mScale; + TEXT_SIZE_MONTH_NUMBER *= mScale; + TEXT_SIZE_EVENT *= mScale; + TEXT_SIZE_EVENT_TITLE *= mScale; + TEXT_SIZE_MORE_EVENTS *= mScale; + TEXT_SIZE_MONTH_NAME *= mScale; + TEXT_SIZE_WEEK_NUM *= mScale; + DAY_SEPARATOR_OUTER_WIDTH *= mScale; + DAY_SEPARATOR_INNER_WIDTH *= mScale; + DAY_SEPARATOR_VERTICAL_LENGTH *= mScale; + DAY_SEPARATOR_VERTICAL_LENGTH_PORTRAIT *= mScale; + EVENT_X_OFFSET_LANDSCAPE *= mScale; + EVENT_Y_OFFSET_LANDSCAPE *= mScale; + EVENT_Y_OFFSET_PORTRAIT *= mScale; + EVENT_SQUARE_WIDTH *= mScale; + EVENT_SQUARE_BORDER *= mScale; + EVENT_LINE_PADDING *= mScale; + EVENT_BOTTOM_PADDING *= mScale; + EVENT_RIGHT_PADDING *= mScale; + DNA_MARGIN *= mScale; + DNA_WIDTH *= mScale; + DNA_ALL_DAY_HEIGHT *= mScale; + DNA_MIN_SEGMENT_HEIGHT *= mScale; + DNA_SIDE_PADDING *= mScale; + DEFAULT_EDGE_SPACING *= mScale; + DNA_ALL_DAY_WIDTH *= mScale; + TODAY_HIGHLIGHT_WIDTH *= mScale; + } + if (!mShowDetailsInMonth) { + TOP_PADDING_MONTH_NUMBER += DNA_ALL_DAY_HEIGHT + DNA_MARGIN; + } + mInitialized = true; + } + mPadding = DEFAULT_EDGE_SPACING; + loadColors(getContext()); + // TODO modify paint properties depending on isMini + + mMonthNumPaint = new Paint(); + mMonthNumPaint.setFakeBoldText(false); + mMonthNumPaint.setAntiAlias(true); + mMonthNumPaint.setTextSize(TEXT_SIZE_MONTH_NUMBER); + mMonthNumPaint.setColor(mMonthNumColor); + mMonthNumPaint.setStyle(Style.FILL); + mMonthNumPaint.setTextAlign(Align.RIGHT); + mMonthNumPaint.setTypeface(Typeface.DEFAULT); + + mMonthNumAscentHeight = (int) (-mMonthNumPaint.ascent() + 0.5f); + mMonthNumHeight = (int) (mMonthNumPaint.descent() - mMonthNumPaint.ascent() + 0.5f); + + mEventPaint = new TextPaint(); + mEventPaint.setFakeBoldText(true); + mEventPaint.setAntiAlias(true); + mEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE); + mEventPaint.setColor(mMonthEventColor); + + mSolidBackgroundEventPaint = new TextPaint(mEventPaint); + mSolidBackgroundEventPaint.setColor(EVENT_TEXT_COLOR); + mFramedEventPaint = new TextPaint(mSolidBackgroundEventPaint); + + mDeclinedEventPaint = new TextPaint(); + mDeclinedEventPaint.setFakeBoldText(true); + mDeclinedEventPaint.setAntiAlias(true); + mDeclinedEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE); + mDeclinedEventPaint.setColor(mMonthDeclinedEventColor); + + mEventAscentHeight = (int) (-mEventPaint.ascent() + 0.5f); + mEventHeight = (int) (mEventPaint.descent() - mEventPaint.ascent() + 0.5f); + + mEventExtrasPaint = new TextPaint(); + mEventExtrasPaint.setFakeBoldText(false); + mEventExtrasPaint.setAntiAlias(true); + mEventExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER); + mEventExtrasPaint.setTextSize(TEXT_SIZE_EVENT); + mEventExtrasPaint.setColor(mMonthEventExtraColor); + mEventExtrasPaint.setStyle(Style.FILL); + mEventExtrasPaint.setTextAlign(Align.LEFT); + mExtrasHeight = (int)(mEventExtrasPaint.descent() - mEventExtrasPaint.ascent() + 0.5f); + mExtrasAscentHeight = (int)(-mEventExtrasPaint.ascent() + 0.5f); + mExtrasDescent = (int)(mEventExtrasPaint.descent() + 0.5f); + + mEventDeclinedExtrasPaint = new TextPaint(); + mEventDeclinedExtrasPaint.setFakeBoldText(false); + mEventDeclinedExtrasPaint.setAntiAlias(true); + mEventDeclinedExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER); + mEventDeclinedExtrasPaint.setTextSize(TEXT_SIZE_EVENT); + mEventDeclinedExtrasPaint.setColor(mMonthDeclinedExtrasColor); + mEventDeclinedExtrasPaint.setStyle(Style.FILL); + mEventDeclinedExtrasPaint.setTextAlign(Align.LEFT); + + mWeekNumPaint = new Paint(); + mWeekNumPaint.setFakeBoldText(false); + mWeekNumPaint.setAntiAlias(true); + mWeekNumPaint.setTextSize(TEXT_SIZE_WEEK_NUM); + mWeekNumPaint.setColor(mWeekNumColor); + mWeekNumPaint.setStyle(Style.FILL); + mWeekNumPaint.setTextAlign(Align.RIGHT); + + mWeekNumAscentHeight = (int) (-mWeekNumPaint.ascent() + 0.5f); + + mDNAAllDayPaint = new Paint(); + mDNATimePaint = new Paint(); + mDNATimePaint.setColor(mMonthBusyBitsBusyTimeColor); + mDNATimePaint.setStyle(Style.FILL_AND_STROKE); + mDNATimePaint.setStrokeWidth(DNA_WIDTH); + mDNATimePaint.setAntiAlias(false); + mDNAAllDayPaint.setColor(mMonthBusyBitsConflictTimeColor); + mDNAAllDayPaint.setStyle(Style.FILL_AND_STROKE); + mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH); + mDNAAllDayPaint.setAntiAlias(false); + + mEventSquarePaint = new Paint(); + mEventSquarePaint.setStrokeWidth(EVENT_SQUARE_BORDER); + mEventSquarePaint.setAntiAlias(false); + + if (DEBUG_LAYOUT) { + Log.d("EXTRA", "mScale=" + mScale); + Log.d("EXTRA", "mMonthNumPaint ascent=" + mMonthNumPaint.ascent() + + " descent=" + mMonthNumPaint.descent() + " int height=" + mMonthNumHeight); + Log.d("EXTRA", "mEventPaint ascent=" + mEventPaint.ascent() + + " descent=" + mEventPaint.descent() + " int height=" + mEventHeight + + " int ascent=" + mEventAscentHeight); + Log.d("EXTRA", "mEventExtrasPaint ascent=" + mEventExtrasPaint.ascent() + + " descent=" + mEventExtrasPaint.descent() + " int height=" + mExtrasHeight); + Log.d("EXTRA", "mWeekNumPaint ascent=" + mWeekNumPaint.ascent() + + " descent=" + mWeekNumPaint.descent()); + } + } + + @Override + public void setWeekParams(HashMap params, String tz) { + super.setWeekParams(params, tz); + + if (params.containsKey(VIEW_PARAMS_ORIENTATION)) { + mOrientation = params.get(VIEW_PARAMS_ORIENTATION); + } + + updateToday(tz); + mNumCells = mNumDays + 1; + + if (params.containsKey(VIEW_PARAMS_ANIMATE_TODAY) && mHasToday) { + synchronized (mAnimatorListener) { + if (mTodayAnimator != null) { + mTodayAnimator.removeAllListeners(); + mTodayAnimator.cancel(); + } + mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha", + Math.max(mAnimateTodayAlpha, 80), 255); + mTodayAnimator.setDuration(150); + mAnimatorListener.setAnimator(mTodayAnimator); + mAnimatorListener.setFadingIn(true); + mTodayAnimator.addListener(mAnimatorListener); + mAnimateToday = true; + mTodayAnimator.start(); + } + } + } + + /** + * @param tz + */ + public boolean updateToday(String tz) { + mToday.timezone = tz; + mToday.setToNow(); + mToday.normalize(true); + int julianToday = Time.getJulianDay(mToday.toMillis(false), mToday.gmtoff); + if (julianToday >= mFirstJulianDay && julianToday < mFirstJulianDay + mNumDays) { + mHasToday = true; + mTodayIndex = julianToday - mFirstJulianDay; + } else { + mHasToday = false; + mTodayIndex = -1; + } + return mHasToday; + } + + public void setAnimateTodayAlpha(int alpha) { + mAnimateTodayAlpha = alpha; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + drawBackground(canvas); + drawWeekNums(canvas); + drawDaySeparators(canvas); + if (mHasToday && mAnimateToday) { + drawToday(canvas); + } + if (mShowDetailsInMonth) { + drawEvents(canvas); + } else { + if (mDna == null && mUnsortedEvents != null) { + createDna(mUnsortedEvents); + } + drawDNA(canvas); + } + drawClick(canvas); + } + + protected void drawToday(Canvas canvas) { + r.top = DAY_SEPARATOR_INNER_WIDTH + (TODAY_HIGHLIGHT_WIDTH / 2); + r.bottom = mHeight - (int) Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f); + p.setStyle(Style.STROKE); + p.setStrokeWidth(TODAY_HIGHLIGHT_WIDTH); + r.left = computeDayLeftPosition(mTodayIndex) + (TODAY_HIGHLIGHT_WIDTH / 2); + r.right = computeDayLeftPosition(mTodayIndex + 1) + - (int) Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f); + p.setColor(mTodayAnimateColor | (mAnimateTodayAlpha << 24)); + canvas.drawRect(r, p); + p.setStyle(Style.FILL); + } + + // TODO move into SimpleWeekView + // Computes the x position for the left side of the given day + private int computeDayLeftPosition(int day) { + int effectiveWidth = mWidth; + int x = 0; + int xOffset = 0; + if (mShowWeekNum) { + xOffset = SPACING_WEEK_NUMBER + mPadding; + effectiveWidth -= xOffset; + } + x = day * effectiveWidth / mNumDays + xOffset; + return x; + } + + @Override + protected void drawDaySeparators(Canvas canvas) { + float lines[] = new float[8 * 4]; + int count = 6 * 4; + int wkNumOffset = 0; + int i = 0; + if (mShowWeekNum) { + // This adds the first line separating the week number + int xOffset = SPACING_WEEK_NUMBER + mPadding; + count += 4; + lines[i++] = xOffset; + lines[i++] = 0; + lines[i++] = xOffset; + lines[i++] = mHeight; + wkNumOffset++; + } + count += 4; + lines[i++] = 0; + lines[i++] = 0; + lines[i++] = mWidth; + lines[i++] = 0; + int y0 = 0; + int y1 = mHeight; + + while (i < count) { + int x = computeDayLeftPosition(i / 4 - wkNumOffset); + lines[i++] = x; + lines[i++] = y0; + lines[i++] = x; + lines[i++] = y1; + } + p.setColor(mDaySeparatorInnerColor); + p.setStrokeWidth(DAY_SEPARATOR_INNER_WIDTH); + canvas.drawLines(lines, 0, count, p); + } + + @Override + protected void drawBackground(Canvas canvas) { + int i = 0; + int offset = 0; + r.top = DAY_SEPARATOR_INNER_WIDTH; + r.bottom = mHeight; + if (mShowWeekNum) { + i++; + offset++; + } + if (!mOddMonth[i]) { + while (++i < mOddMonth.length && !mOddMonth[i]) + ; + r.right = computeDayLeftPosition(i - offset); + r.left = 0; + p.setColor(mMonthBGOtherColor); + canvas.drawRect(r, p); + // compute left edge for i, set up r, draw + } else if (!mOddMonth[(i = mOddMonth.length - 1)]) { + while (--i >= offset && !mOddMonth[i]) + ; + i++; + // compute left edge for i, set up r, draw + r.right = mWidth; + r.left = computeDayLeftPosition(i - offset); + p.setColor(mMonthBGOtherColor); + canvas.drawRect(r, p); + } + if (mHasToday) { + p.setColor(mMonthBGTodayColor); + r.left = computeDayLeftPosition(mTodayIndex); + r.right = computeDayLeftPosition(mTodayIndex + 1); + canvas.drawRect(r, p); + } + } + + // Draw the "clicked" color on the tapped day + private void drawClick(Canvas canvas) { + if (mClickedDayIndex != -1) { + int alpha = p.getAlpha(); + p.setColor(mClickedDayColor); + p.setAlpha(mClickedAlpha); + r.left = computeDayLeftPosition(mClickedDayIndex); + r.right = computeDayLeftPosition(mClickedDayIndex + 1); + r.top = DAY_SEPARATOR_INNER_WIDTH; + r.bottom = mHeight; + canvas.drawRect(r, p); + p.setAlpha(alpha); + } + } + + @Override + protected void drawWeekNums(Canvas canvas) { + int y; + + int i = 0; + int offset = -1; + int todayIndex = mTodayIndex; + int x = 0; + int numCount = mNumDays; + if (mShowWeekNum) { + x = SIDE_PADDING_WEEK_NUMBER + mPadding; + y = mWeekNumAscentHeight + TOP_PADDING_WEEK_NUMBER; + canvas.drawText(mDayNumbers[0], x, y, mWeekNumPaint); + numCount++; + i++; + todayIndex++; + offset++; + + } + + y = mMonthNumAscentHeight + TOP_PADDING_MONTH_NUMBER; + + boolean isFocusMonth = mFocusDay[i]; + boolean isBold = false; + mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); + for (; i < numCount; i++) { + if (mHasToday && todayIndex == i) { + mMonthNumPaint.setColor(mMonthNumTodayColor); + mMonthNumPaint.setFakeBoldText(isBold = true); + if (i + 1 < numCount) { + // Make sure the color will be set back on the next + // iteration + isFocusMonth = !mFocusDay[i + 1]; + } + } else if (mFocusDay[i] != isFocusMonth) { + isFocusMonth = mFocusDay[i]; + mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); + } + x = computeDayLeftPosition(i - offset) - (SIDE_PADDING_MONTH_NUMBER); + canvas.drawText(mDayNumbers[i], x, y, mMonthNumPaint); + if (isBold) { + mMonthNumPaint.setFakeBoldText(isBold = false); + } + } + } + + protected void drawEvents(Canvas canvas) { + if (mEvents == null) { + return; + } + + int day = -1; + for (ArrayList eventDay : mEvents) { + day++; + if (eventDay == null || eventDay.size() == 0) { + continue; + } + int ySquare; + int xSquare = computeDayLeftPosition(day) + SIDE_PADDING_MONTH_NUMBER + 1; + int rightEdge = computeDayLeftPosition(day + 1); + + if (mOrientation == Configuration.ORIENTATION_PORTRAIT) { + ySquare = EVENT_Y_OFFSET_PORTRAIT + mMonthNumHeight + TOP_PADDING_MONTH_NUMBER; + rightEdge -= SIDE_PADDING_MONTH_NUMBER + 1; + } else { + ySquare = EVENT_Y_OFFSET_LANDSCAPE; + rightEdge -= EVENT_X_OFFSET_LANDSCAPE; + } + + // Determine if everything will fit when time ranges are shown. + boolean showTimes = true; + Iterator iter = eventDay.iterator(); + int yTest = ySquare; + while (iter.hasNext()) { + Event event = iter.next(); + int newY = drawEvent(canvas, event, xSquare, yTest, rightEdge, iter.hasNext(), + showTimes, /*doDraw*/ false); + if (newY == yTest) { + showTimes = false; + break; + } + yTest = newY; + } + + int eventCount = 0; + iter = eventDay.iterator(); + while (iter.hasNext()) { + Event event = iter.next(); + int newY = drawEvent(canvas, event, xSquare, ySquare, rightEdge, iter.hasNext(), + showTimes, /*doDraw*/ true); + if (newY == ySquare) { + break; + } + eventCount++; + ySquare = newY; + } + + int remaining = eventDay.size() - eventCount; + if (remaining > 0) { + drawMoreEvents(canvas, remaining, xSquare); + } + } + } + + protected int addChipOutline(FloatRef lines, int count, int x, int y) { + lines.ensureSize(count + 16); + // top of box + lines.array[count++] = x; + lines.array[count++] = y; + lines.array[count++] = x + EVENT_SQUARE_WIDTH; + lines.array[count++] = y; + // right side of box + lines.array[count++] = x + EVENT_SQUARE_WIDTH; + lines.array[count++] = y; + lines.array[count++] = x + EVENT_SQUARE_WIDTH; + lines.array[count++] = y + EVENT_SQUARE_WIDTH; + // left side of box + lines.array[count++] = x; + lines.array[count++] = y; + lines.array[count++] = x; + lines.array[count++] = y + EVENT_SQUARE_WIDTH + 1; + // bottom of box + lines.array[count++] = x; + lines.array[count++] = y + EVENT_SQUARE_WIDTH; + lines.array[count++] = x + EVENT_SQUARE_WIDTH + 1; + lines.array[count++] = y + EVENT_SQUARE_WIDTH; + + return count; + } + + /** + * Attempts to draw the given event. Returns the y for the next event or the + * original y if the event will not fit. An event is considered to not fit + * if the event and its extras won't fit or if there are more events and the + * more events line would not fit after drawing this event. + * + * @param canvas the canvas to draw on + * @param event the event to draw + * @param x the top left corner for this event's color chip + * @param y the top left corner for this event's color chip + * @param rightEdge the rightmost point we're allowed to draw on (exclusive) + * @param moreEvents indicates whether additional events will follow this one + * @param showTimes if set, a second line with a time range will be displayed for non-all-day + * events + * @param doDraw if set, do the actual drawing; otherwise this just computes the height + * and returns + * @return the y for the next event or the original y if it won't fit + */ + protected int drawEvent(Canvas canvas, Event event, int x, int y, int rightEdge, + boolean moreEvents, boolean showTimes, boolean doDraw) { + /* + * Vertical layout: + * (top of box) + * a. EVENT_Y_OFFSET_LANDSCAPE or portrait equivalent + * b. Event title: mEventHeight for a normal event, + 2xBORDER_SPACE for all-day event + * c. [optional] Time range (mExtrasHeight) + * d. EVENT_LINE_PADDING + * + * Repeat (b,c,d) as needed and space allows. If we have more events than fit, we need + * to leave room for something like "+2" at the bottom: + * + * e. "+ more" line (mExtrasHeight) + * + * f. EVENT_BOTTOM_PADDING (overlaps EVENT_LINE_PADDING) + * (bottom of box) + */ + final int BORDER_SPACE = EVENT_SQUARE_BORDER + 1; // want a 1-pixel gap inside border + final int STROKE_WIDTH_ADJ = EVENT_SQUARE_BORDER / 2; // adjust bounds for stroke width + boolean allDay = event.allDay; + int eventRequiredSpace = mEventHeight; + if (allDay) { + // Add a few pixels for the box we draw around all-day events. + eventRequiredSpace += BORDER_SPACE * 2; + } else if (showTimes) { + // Need room for the "1pm - 2pm" line. + eventRequiredSpace += mExtrasHeight; + } + int reservedSpace = EVENT_BOTTOM_PADDING; // leave a bit of room at the bottom + if (moreEvents) { + // More events follow. Leave a bit of space between events. + eventRequiredSpace += EVENT_LINE_PADDING; + + // Make sure we have room for the "+ more" line. (The "+ more" line is expected + // to be <= the height of an event line, so we won't show "+1" when we could be + // showing the event.) + reservedSpace += mExtrasHeight; + } + + if (y + eventRequiredSpace + reservedSpace > mHeight) { + // Not enough space, return original y + return y; + } else if (!doDraw) { + return y + eventRequiredSpace; + } + + boolean isDeclined = event.selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED; + int color = event.color; + if (isDeclined) { + color = Utils.getDeclinedColorFromColor(color); + } + + int textX, textY, textRightEdge; + + if (allDay) { + // We shift the render offset "inward", because drawRect with a stroke width greater + // than 1 draws outside the specified bounds. (We don't adjust the left edge, since + // we want to match the existing appearance of the "event square".) + r.left = x; + r.right = rightEdge - STROKE_WIDTH_ADJ; + r.top = y + STROKE_WIDTH_ADJ; + r.bottom = y + mEventHeight + BORDER_SPACE * 2 - STROKE_WIDTH_ADJ; + textX = x + BORDER_SPACE; + textY = y + mEventAscentHeight + BORDER_SPACE; + textRightEdge = rightEdge - BORDER_SPACE; + } else { + r.left = x; + r.right = x + EVENT_SQUARE_WIDTH; + r.bottom = y + mEventAscentHeight; + r.top = r.bottom - EVENT_SQUARE_WIDTH; + textX = x + EVENT_SQUARE_WIDTH + EVENT_RIGHT_PADDING; + textY = y + mEventAscentHeight; + textRightEdge = rightEdge; + } + + Style boxStyle = Style.STROKE; + boolean solidBackground = false; + if (event.selfAttendeeStatus != Attendees.ATTENDEE_STATUS_INVITED) { + boxStyle = Style.FILL_AND_STROKE; + if (allDay) { + solidBackground = true; + } + } + mEventSquarePaint.setStyle(boxStyle); + mEventSquarePaint.setColor(color); + canvas.drawRect(r, mEventSquarePaint); + + float avail = textRightEdge - textX; + CharSequence text = TextUtils.ellipsize( + event.title, mEventPaint, avail, TextUtils.TruncateAt.END); + Paint textPaint; + if (solidBackground) { + // Text color needs to contrast with solid background. + textPaint = mSolidBackgroundEventPaint; + } else if (isDeclined) { + // Use "declined event" color. + textPaint = mDeclinedEventPaint; + } else if (allDay) { + // Text inside frame is same color as frame. + mFramedEventPaint.setColor(color); + textPaint = mFramedEventPaint; + } else { + // Use generic event text color. + textPaint = mEventPaint; + } + canvas.drawText(text.toString(), textX, textY, textPaint); + y += mEventHeight; + if (allDay) { + y += BORDER_SPACE * 2; + } + + if (showTimes && !allDay) { + // show start/end time, e.g. "1pm - 2pm" + textY = y + mExtrasAscentHeight; + mStringBuilder.setLength(0); + text = DateUtils.formatDateRange(getContext(), mFormatter, event.startMillis, + event.endMillis, DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL, + Utils.getTimeZone(getContext(), null)).toString(); + text = TextUtils.ellipsize(text, mEventExtrasPaint, avail, TextUtils.TruncateAt.END); + canvas.drawText(text.toString(), textX, textY, isDeclined ? mEventDeclinedExtrasPaint + : mEventExtrasPaint); + y += mExtrasHeight; + } + + y += EVENT_LINE_PADDING; + + return y; + } + + protected void drawMoreEvents(Canvas canvas, int remainingEvents, int x) { + int y = mHeight - (mExtrasDescent + EVENT_BOTTOM_PADDING); + String text = getContext().getResources().getQuantityString( + R.plurals.month_more_events, remainingEvents); + mEventExtrasPaint.setAntiAlias(true); + mEventExtrasPaint.setFakeBoldText(true); + canvas.drawText(String.format(text, remainingEvents), x, y, mEventExtrasPaint); + mEventExtrasPaint.setFakeBoldText(false); + } + + /** + * Draws a line showing busy times in each day of week The method draws + * non-conflicting times in the event color and times with conflicting + * events in the dna conflict color defined in colors. + * + * @param canvas + */ + protected void drawDNA(Canvas canvas) { + // Draw event and conflict times + if (mDna != null) { + for (Utils.DNAStrand strand : mDna.values()) { + if (strand.color == CONFLICT_COLOR || strand.points == null + || strand.points.length == 0) { + continue; + } + mDNATimePaint.setColor(strand.color); + canvas.drawLines(strand.points, mDNATimePaint); + } + // Draw black last to make sure it's on top + Utils.DNAStrand strand = mDna.get(CONFLICT_COLOR); + if (strand != null && strand.points != null && strand.points.length != 0) { + mDNATimePaint.setColor(strand.color); + canvas.drawLines(strand.points, mDNATimePaint); + } + if (mDayXs == null) { + return; + } + int numDays = mDayXs.length; + int xOffset = (DNA_ALL_DAY_WIDTH - DNA_WIDTH) / 2; + if (strand != null && strand.allDays != null && strand.allDays.length == numDays) { + for (int i = 0; i < numDays; i++) { + // this adds at most 7 draws. We could sort it by color and + // build an array instead but this is easier. + if (strand.allDays[i] != 0) { + mDNAAllDayPaint.setColor(strand.allDays[i]); + canvas.drawLine(mDayXs[i] + xOffset, DNA_MARGIN, mDayXs[i] + xOffset, + DNA_MARGIN + DNA_ALL_DAY_HEIGHT, mDNAAllDayPaint); + } + } + } + } + } + + @Override + protected void updateSelectionPositions() { + if (mHasSelectedDay) { + int selectedPosition = mSelectedDay - mWeekStart; + if (selectedPosition < 0) { + selectedPosition += 7; + } + int effectiveWidth = mWidth - mPadding * 2; + effectiveWidth -= SPACING_WEEK_NUMBER; + mSelectedLeft = selectedPosition * effectiveWidth / mNumDays + mPadding; + mSelectedRight = (selectedPosition + 1) * effectiveWidth / mNumDays + mPadding; + mSelectedLeft += SPACING_WEEK_NUMBER; + mSelectedRight += SPACING_WEEK_NUMBER; + } + } + + public int getDayIndexFromLocation(float x) { + int dayStart = mShowWeekNum ? SPACING_WEEK_NUMBER + mPadding : mPadding; + if (x < dayStart || x > mWidth - mPadding) { + return -1; + } + // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels + return ((int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding))); + } + + @Override + public Time getDayFromLocation(float x) { + int dayPosition = getDayIndexFromLocation(x); + if (dayPosition == -1) { + return null; + } + int day = mFirstJulianDay + dayPosition; + + Time time = new Time(mTimeZone); + if (mWeek == 0) { + // This week is weird... + if (day < Time.EPOCH_JULIAN_DAY) { + day++; + } else if (day == Time.EPOCH_JULIAN_DAY) { + time.set(1, 0, 1970); + time.normalize(true); + return time; + } + } + + time.setJulianDay(day); + return time; + } + + @Override + public boolean onHoverEvent(MotionEvent event) { + Context context = getContext(); + // only send accessibility events if accessibility and exploration are + // on. + AccessibilityManager am = (AccessibilityManager) context + .getSystemService(Service.ACCESSIBILITY_SERVICE); + if (!am.isEnabled() || !am.isTouchExplorationEnabled()) { + return super.onHoverEvent(event); + } + if (event.getAction() != MotionEvent.ACTION_HOVER_EXIT) { + Time hover = getDayFromLocation(event.getX()); + if (hover != null + && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) != 0)) { + Long millis = hover.toMillis(true); + String date = Utils.formatDateRange(context, millis, millis, + DateUtils.FORMAT_SHOW_DATE); + AccessibilityEvent accessEvent = AccessibilityEvent + .obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); + accessEvent.getText().add(date); + if (mShowDetailsInMonth && mEvents != null) { + int dayStart = SPACING_WEEK_NUMBER + mPadding; + int dayPosition = (int) ((event.getX() - dayStart) * mNumDays / (mWidth + - dayStart - mPadding)); + ArrayList events = mEvents.get(dayPosition); + List text = accessEvent.getText(); + for (Event e : events) { + text.add(e.getTitleAndLocation() + ". "); + int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; + if (!e.allDay) { + flags |= DateUtils.FORMAT_SHOW_TIME; + if (DateFormat.is24HourFormat(context)) { + flags |= DateUtils.FORMAT_24HOUR; + } + } else { + flags |= DateUtils.FORMAT_UTC; + } + text.add(Utils.formatDateRange(context, e.startMillis, e.endMillis, + flags) + ". "); + } + } + sendAccessibilityEventUnchecked(accessEvent); + mLastHoverTime = hover; + } + } + return true; + } + + public void setClickedDay(float xLocation) { + mClickedDayIndex = getDayIndexFromLocation(xLocation); + invalidate(); + } + public void clearClickedDay() { + mClickedDayIndex = -1; + invalidate(); + } +} -- cgit v1.2.3 From 42e4b43133c4f866e0729438fb38bebc6d03b0a4 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Mon, 12 Jul 2021 18:58:19 +0000 Subject: AOSP/Calendar - Initial conversion of MonthWeekEventsView as performed by Android Studio's automatic converter. Test: none, no functional change in this commit when merged with corresponding Kotlin conversion. Change-Id: Ie0face86c3e53b34b14785e248d894c2ebaa82a8 --- .../android/calendar/month/MonthWeekEventsView.kt | 1449 +++++++++----------- 1 file changed, 678 insertions(+), 771 deletions(-) diff --git a/src/com/android/calendar/month/MonthWeekEventsView.kt b/src/com/android/calendar/month/MonthWeekEventsView.kt index 071daee3..adaf70dc 100644 --- a/src/com/android/calendar/month/MonthWeekEventsView.kt +++ b/src/com/android/calendar/month/MonthWeekEventsView.kt @@ -13,252 +13,148 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.calendar.month -package com.android.calendar.month; +import com.android.calendar.Event -import com.android.calendar.Event; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.app.Service; -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Paint.Style; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.provider.CalendarContract.Attendees; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.MotionEvent; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Formatter; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; - -public class MonthWeekEventsView extends SimpleWeekView { - - private static final String TAG = "MonthView"; - - private static final boolean DEBUG_LAYOUT = false; - - public static final String VIEW_PARAMS_ORIENTATION = "orientation"; - public static final String VIEW_PARAMS_ANIMATE_TODAY = "animate_today"; - - /* NOTE: these are not constants, and may be multiplied by a scale factor */ - private static int TEXT_SIZE_MONTH_NUMBER = 32; - private static int TEXT_SIZE_EVENT = 12; - private static int TEXT_SIZE_EVENT_TITLE = 14; - private static int TEXT_SIZE_MORE_EVENTS = 12; - private static int TEXT_SIZE_MONTH_NAME = 14; - private static int TEXT_SIZE_WEEK_NUM = 12; - - private static int DNA_MARGIN = 4; - private static int DNA_ALL_DAY_HEIGHT = 4; - private static int DNA_MIN_SEGMENT_HEIGHT = 4; - private static int DNA_WIDTH = 8; - private static int DNA_ALL_DAY_WIDTH = 32; - private static int DNA_SIDE_PADDING = 6; - private static int CONFLICT_COLOR = Color.BLACK; - private static int EVENT_TEXT_COLOR = Color.WHITE; - - private static int DEFAULT_EDGE_SPACING = 0; - private static int SIDE_PADDING_MONTH_NUMBER = 4; - private static int TOP_PADDING_MONTH_NUMBER = 4; - private static int TOP_PADDING_WEEK_NUMBER = 4; - private static int SIDE_PADDING_WEEK_NUMBER = 20; - private static int DAY_SEPARATOR_OUTER_WIDTH = 0; - private static int DAY_SEPARATOR_INNER_WIDTH = 1; - private static int DAY_SEPARATOR_VERTICAL_LENGTH = 53; - private static int DAY_SEPARATOR_VERTICAL_LENGTH_PORTRAIT = 64; - private static int MIN_WEEK_WIDTH = 50; - - private static int EVENT_X_OFFSET_LANDSCAPE = 38; - private static int EVENT_Y_OFFSET_LANDSCAPE = 8; - private static int EVENT_Y_OFFSET_PORTRAIT = 7; - private static int EVENT_SQUARE_WIDTH = 10; - private static int EVENT_SQUARE_BORDER = 2; - private static int EVENT_LINE_PADDING = 2; - private static int EVENT_RIGHT_PADDING = 4; - private static int EVENT_BOTTOM_PADDING = 3; - - private static int TODAY_HIGHLIGHT_WIDTH = 2; - - private static int SPACING_WEEK_NUMBER = 24; - private static boolean mInitialized = false; - private static boolean mShowDetailsInMonth; +class MonthWeekEventsView +/** + * Shows up as an error if we don't include this. + */ +(context: Context?) : SimpleWeekView(context) { + protected var mToday: Time = Time() + protected var mHasToday = false + protected var mTodayIndex = -1 + protected var mOrientation: Int = Configuration.ORIENTATION_LANDSCAPE + protected var mEvents: List>? = null + protected var mUnsortedEvents: ArrayList? = null + var mDna: HashMap? = null - protected Time mToday = new Time(); - protected boolean mHasToday = false; - protected int mTodayIndex = -1; - protected int mOrientation = Configuration.ORIENTATION_LANDSCAPE; - protected List> mEvents = null; - protected ArrayList mUnsortedEvents = null; - HashMap mDna = null; // This is for drawing the outlines around event chips and supports up to 10 // events being drawn on each day. The code will expand this if necessary. - protected FloatRef mEventOutlines = new FloatRef(10 * 4 * 4 * 7); - - - - protected static StringBuilder mStringBuilder = new StringBuilder(50); - // TODO recreate formatter when locale changes - protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); - - protected Paint mMonthNamePaint; - protected TextPaint mEventPaint; - protected TextPaint mSolidBackgroundEventPaint; - protected TextPaint mFramedEventPaint; - protected TextPaint mDeclinedEventPaint; - protected TextPaint mEventExtrasPaint; - protected TextPaint mEventDeclinedExtrasPaint; - protected Paint mWeekNumPaint; - protected Paint mDNAAllDayPaint; - protected Paint mDNATimePaint; - protected Paint mEventSquarePaint; - - - protected Drawable mTodayDrawable; - - protected int mMonthNumHeight; - protected int mMonthNumAscentHeight; - protected int mEventHeight; - protected int mEventAscentHeight; - protected int mExtrasHeight; - protected int mExtrasAscentHeight; - protected int mExtrasDescent; - protected int mWeekNumAscentHeight; - - protected int mMonthBGColor; - protected int mMonthBGOtherColor; - protected int mMonthBGTodayColor; - protected int mMonthNumColor; - protected int mMonthNumOtherColor; - protected int mMonthNumTodayColor; - protected int mMonthNameColor; - protected int mMonthNameOtherColor; - protected int mMonthEventColor; - protected int mMonthDeclinedEventColor; - protected int mMonthDeclinedExtrasColor; - protected int mMonthEventExtraColor; - protected int mMonthEventOtherColor; - protected int mMonthEventExtraOtherColor; - protected int mMonthWeekNumColor; - protected int mMonthBusyBitsBgColor; - protected int mMonthBusyBitsBusyTimeColor; - protected int mMonthBusyBitsConflictTimeColor; - private int mClickedDayIndex = -1; - private int mClickedDayColor; - private static final int mClickedAlpha = 128; - - protected int mEventChipOutlineColor = 0xFFFFFFFF; - protected int mDaySeparatorInnerColor; - protected int mTodayAnimateColor; - - private boolean mAnimateToday; - private int mAnimateTodayAlpha = 0; - private ObjectAnimator mTodayAnimator = null; - - private final TodayAnimatorListener mAnimatorListener = new TodayAnimatorListener(); - - class TodayAnimatorListener extends AnimatorListenerAdapter { - private volatile Animator mAnimator = null; - private volatile boolean mFadingIn = false; - + protected var mEventOutlines = FloatRef(10 * 4 * 4 * 7) + protected var mMonthNamePaint: Paint? = null + protected var mEventPaint: TextPaint? = null + protected var mSolidBackgroundEventPaint: TextPaint? = null + protected var mFramedEventPaint: TextPaint? = null + protected var mDeclinedEventPaint: TextPaint? = null + protected var mEventExtrasPaint: TextPaint? = null + protected var mEventDeclinedExtrasPaint: TextPaint? = null + protected var mWeekNumPaint: Paint? = null + protected var mDNAAllDayPaint: Paint? = null + protected var mDNATimePaint: Paint? = null + protected var mEventSquarePaint: Paint? = null + protected var mTodayDrawable: Drawable? = null + protected var mMonthNumHeight = 0 + protected var mMonthNumAscentHeight = 0 + protected var mEventHeight = 0 + protected var mEventAscentHeight = 0 + protected var mExtrasHeight = 0 + protected var mExtrasAscentHeight = 0 + protected var mExtrasDescent = 0 + protected var mWeekNumAscentHeight = 0 + protected var mMonthBGColor = 0 + protected var mMonthBGOtherColor = 0 + protected var mMonthBGTodayColor = 0 + protected var mMonthNumColor = 0 + protected var mMonthNumOtherColor = 0 + protected var mMonthNumTodayColor = 0 + protected var mMonthNameColor = 0 + protected var mMonthNameOtherColor = 0 + protected var mMonthEventColor = 0 + protected var mMonthDeclinedEventColor = 0 + protected var mMonthDeclinedExtrasColor = 0 + protected var mMonthEventExtraColor = 0 + protected var mMonthEventOtherColor = 0 + protected var mMonthEventExtraOtherColor = 0 + protected var mMonthWeekNumColor = 0 + protected var mMonthBusyBitsBgColor = 0 + protected var mMonthBusyBitsBusyTimeColor = 0 + protected var mMonthBusyBitsConflictTimeColor = 0 + private var mClickedDayIndex = -1 + private var mClickedDayColor = 0 + protected var mEventChipOutlineColor = -0x1 + protected var mDaySeparatorInnerColor = 0 + protected var mTodayAnimateColor = 0 + private var mAnimateToday = false + private var mAnimateTodayAlpha = 0 + private var mTodayAnimator: ObjectAnimator? = null + private val mAnimatorListener: TodayAnimatorListener = TodayAnimatorListener() + + internal inner class TodayAnimatorListener : AnimatorListenerAdapter() { + @Volatile + private var mAnimator: Animator? = null + + @Volatile + private var mFadingIn = false @Override - public void onAnimationEnd(Animator animation) { - synchronized (this) { - if (mAnimator != animation) { - animation.removeAllListeners(); - animation.cancel(); - return; + fun onAnimationEnd(animation: Animator) { + synchronized(this) { + if (mAnimator !== animation) { + animation.removeAllListeners() + animation.cancel() + return } if (mFadingIn) { if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners(); - mTodayAnimator.cancel(); + mTodayAnimator.removeAllListeners() + mTodayAnimator.cancel() } - mTodayAnimator = ObjectAnimator.ofInt(MonthWeekEventsView.this, - "animateTodayAlpha", 255, 0); - mAnimator = mTodayAnimator; - mFadingIn = false; - mTodayAnimator.addListener(this); - mTodayAnimator.setDuration(600); - mTodayAnimator.start(); + mTodayAnimator = ObjectAnimator.ofInt(this@MonthWeekEventsView, + "animateTodayAlpha", 255, 0) + mAnimator = mTodayAnimator + mFadingIn = false + mTodayAnimator.addListener(this) + mTodayAnimator.setDuration(600) + mTodayAnimator.start() } else { - mAnimateToday = false; - mAnimateTodayAlpha = 0; - mAnimator.removeAllListeners(); - mAnimator = null; - mTodayAnimator = null; - invalidate(); + mAnimateToday = false + mAnimateTodayAlpha = 0 + mAnimator.removeAllListeners() + mAnimator = null + mTodayAnimator = null + invalidate() } } } - public void setAnimator(Animator animation) { - mAnimator = animation; + fun setAnimator(animation: Animator?) { + mAnimator = animation } - public void setFadingIn(boolean fadingIn) { - mFadingIn = fadingIn; + fun setFadingIn(fadingIn: Boolean) { + mFadingIn = fadingIn } - } - private int[] mDayXs; + private var mDayXs: IntArray? /** * This provides a reference to a float array which allows for easy size * checking and reallocation. Used for drawing lines. */ - private class FloatRef { - float[] array; - - public FloatRef(int size) { - array = new float[size]; - } - - public void ensureSize(int newSize) { - if (newSize >= array.length) { + private inner class FloatRef(size: Int) { + var array: FloatArray + fun ensureSize(newSize: Int) { + if (newSize >= array.size) { // Add enough space for 7 more boxes to be drawn - array = Arrays.copyOf(array, newSize + 16 * 7); + array = Arrays.copyOf(array, newSize + 16 * 7) } } - } - /** - * Shows up as an error if we don't include this. - */ - public MonthWeekEventsView(Context context) { - super(context); + init { + array = FloatArray(size) + } } // Sets the list of events for this week. Takes a sorted list of arrays // divided up by day for generating the large month version and the full // arraylist sorted by start time to generate the dna version. - public void setEvents(List> sortedEvents, ArrayList unsortedEvents) { - setEvents(sortedEvents); + fun setEvents(sortedEvents: List>?, unsortedEvents: ArrayList?) { + setEvents(sortedEvents) // The MIN_WEEK_WIDTH is a hack to prevent the view from trying to // generate dna bits before its width has been fixed. - createDna(unsortedEvents); + createDna(unsortedEvents) } /** @@ -266,75 +162,73 @@ public class MonthWeekEventsView extends SimpleWeekView { * isn't in a state that will create a valid set of dna yet (such as the * views width not being set correctly yet). */ - public void createDna(ArrayList unsortedEvents) { + fun createDna(unsortedEvents: ArrayList?) { if (unsortedEvents == null || mWidth <= MIN_WEEK_WIDTH || getContext() == null) { // Stash the list of events for use when this view is ready, or // just clear it if a null set has been passed to this view - mUnsortedEvents = unsortedEvents; - mDna = null; - return; + mUnsortedEvents = unsortedEvents + mDna = null + return } else { // clear the cached set of events since we're ready to build it now - mUnsortedEvents = null; + mUnsortedEvents = null } // Create the drawing coordinates for dna if (!mShowDetailsInMonth) { - int numDays = mEvents.size(); - int effectiveWidth = mWidth - mPadding * 2; + val numDays: Int = mEvents!!.size() + var effectiveWidth: Int = mWidth - mPadding * 2 if (mShowWeekNum) { - effectiveWidth -= SPACING_WEEK_NUMBER; + effectiveWidth -= SPACING_WEEK_NUMBER } - DNA_ALL_DAY_WIDTH = effectiveWidth / numDays - 2 * DNA_SIDE_PADDING; - mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH); - mDayXs = new int[numDays]; - for (int day = 0; day < numDays; day++) { - mDayXs[day] = computeDayLeftPosition(day) + DNA_WIDTH / 2 + DNA_SIDE_PADDING; - + DNA_ALL_DAY_WIDTH = effectiveWidth / numDays - 2 * DNA_SIDE_PADDING + mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH) + mDayXs = IntArray(numDays) + for (day in 0 until numDays) { + mDayXs!![day] = computeDayLeftPosition(day) + DNA_WIDTH / 2 + DNA_SIDE_PADDING } - - int top = DAY_SEPARATOR_INNER_WIDTH + DNA_MARGIN + DNA_ALL_DAY_HEIGHT + 1; - int bottom = mHeight - DNA_MARGIN; + val top = DAY_SEPARATOR_INNER_WIDTH + DNA_MARGIN + DNA_ALL_DAY_HEIGHT + 1 + val bottom: Int = mHeight - DNA_MARGIN mDna = Utils.createDNAStrands(mFirstJulianDay, unsortedEvents, top, bottom, - DNA_MIN_SEGMENT_HEIGHT, mDayXs, getContext()); + DNA_MIN_SEGMENT_HEIGHT, mDayXs, getContext()) } } - public void setEvents(List> sortedEvents) { - mEvents = sortedEvents; + fun setEvents(sortedEvents: List>?) { + mEvents = sortedEvents if (sortedEvents == null) { - return; + return } - if (sortedEvents.size() != mNumDays) { + if (sortedEvents.size() !== mNumDays) { if (Log.isLoggable(TAG, Log.ERROR)) { - Log.wtf(TAG, "Events size must be same as days displayed: size=" - + sortedEvents.size() + " days=" + mNumDays); + Log.wtf(TAG, ("Events size must be same as days displayed: size=" + + sortedEvents.size()) + " days=" + mNumDays) } - mEvents = null; - return; + mEvents = null + return } } - protected void loadColors(Context context) { - Resources res = context.getResources(); - mMonthWeekNumColor = res.getColor(R.color.month_week_num_color); - mMonthNumColor = res.getColor(R.color.month_day_number); - mMonthNumOtherColor = res.getColor(R.color.month_day_number_other); - mMonthNumTodayColor = res.getColor(R.color.month_today_number); - mMonthNameColor = mMonthNumColor; - mMonthNameOtherColor = mMonthNumOtherColor; - mMonthEventColor = res.getColor(R.color.month_event_color); - mMonthDeclinedEventColor = res.getColor(R.color.agenda_item_declined_color); - mMonthDeclinedExtrasColor = res.getColor(R.color.agenda_item_where_declined_text_color); - mMonthEventExtraColor = res.getColor(R.color.month_event_extra_color); - mMonthEventOtherColor = res.getColor(R.color.month_event_other_color); - mMonthEventExtraOtherColor = res.getColor(R.color.month_event_extra_other_color); - mMonthBGTodayColor = res.getColor(R.color.month_today_bgcolor); - mMonthBGOtherColor = res.getColor(R.color.month_other_bgcolor); - mMonthBGColor = res.getColor(R.color.month_bgcolor); - mDaySeparatorInnerColor = res.getColor(R.color.month_grid_lines); - mTodayAnimateColor = res.getColor(R.color.today_highlight_color); - mClickedDayColor = res.getColor(R.color.day_clicked_background_color); - mTodayDrawable = res.getDrawable(R.drawable.today_blue_week_holo_light); + protected fun loadColors(context: Context) { + val res: Resources = context.getResources() + mMonthWeekNumColor = res.getColor(R.color.month_week_num_color) + mMonthNumColor = res.getColor(R.color.month_day_number) + mMonthNumOtherColor = res.getColor(R.color.month_day_number_other) + mMonthNumTodayColor = res.getColor(R.color.month_today_number) + mMonthNameColor = mMonthNumColor + mMonthNameOtherColor = mMonthNumOtherColor + mMonthEventColor = res.getColor(R.color.month_event_color) + mMonthDeclinedEventColor = res.getColor(R.color.agenda_item_declined_color) + mMonthDeclinedExtrasColor = res.getColor(R.color.agenda_item_where_declined_text_color) + mMonthEventExtraColor = res.getColor(R.color.month_event_extra_color) + mMonthEventOtherColor = res.getColor(R.color.month_event_other_color) + mMonthEventExtraOtherColor = res.getColor(R.color.month_event_extra_other_color) + mMonthBGTodayColor = res.getColor(R.color.month_today_bgcolor) + mMonthBGOtherColor = res.getColor(R.color.month_other_bgcolor) + mMonthBGColor = res.getColor(R.color.month_bgcolor) + mDaySeparatorInnerColor = res.getColor(R.color.month_grid_lines) + mTodayAnimateColor = res.getColor(R.color.today_highlight_color) + mClickedDayColor = res.getColor(R.color.day_clicked_background_color) + mTodayDrawable = res.getDrawable(R.drawable.today_blue_week_holo_light) } /** @@ -342,175 +236,158 @@ public class MonthWeekEventsView extends SimpleWeekView { * want to use a different paint. */ @Override - protected void initView() { - super.initView(); - + protected fun initView() { + super.initView() if (!mInitialized) { - Resources resources = getContext().getResources(); - mShowDetailsInMonth = Utils.getConfigBool(getContext(), R.bool.show_details_in_month); - TEXT_SIZE_EVENT_TITLE = resources.getInteger(R.integer.text_size_event_title); - TEXT_SIZE_MONTH_NUMBER = resources.getInteger(R.integer.text_size_month_number); - SIDE_PADDING_MONTH_NUMBER = resources.getInteger(R.integer.month_day_number_margin); - CONFLICT_COLOR = resources.getColor(R.color.month_dna_conflict_time_color); - EVENT_TEXT_COLOR = resources.getColor(R.color.calendar_event_text_color); - if (mScale != 1) { - TOP_PADDING_MONTH_NUMBER *= mScale; - TOP_PADDING_WEEK_NUMBER *= mScale; - SIDE_PADDING_MONTH_NUMBER *= mScale; - SIDE_PADDING_WEEK_NUMBER *= mScale; - SPACING_WEEK_NUMBER *= mScale; - TEXT_SIZE_MONTH_NUMBER *= mScale; - TEXT_SIZE_EVENT *= mScale; - TEXT_SIZE_EVENT_TITLE *= mScale; - TEXT_SIZE_MORE_EVENTS *= mScale; - TEXT_SIZE_MONTH_NAME *= mScale; - TEXT_SIZE_WEEK_NUM *= mScale; - DAY_SEPARATOR_OUTER_WIDTH *= mScale; - DAY_SEPARATOR_INNER_WIDTH *= mScale; - DAY_SEPARATOR_VERTICAL_LENGTH *= mScale; - DAY_SEPARATOR_VERTICAL_LENGTH_PORTRAIT *= mScale; - EVENT_X_OFFSET_LANDSCAPE *= mScale; - EVENT_Y_OFFSET_LANDSCAPE *= mScale; - EVENT_Y_OFFSET_PORTRAIT *= mScale; - EVENT_SQUARE_WIDTH *= mScale; - EVENT_SQUARE_BORDER *= mScale; - EVENT_LINE_PADDING *= mScale; - EVENT_BOTTOM_PADDING *= mScale; - EVENT_RIGHT_PADDING *= mScale; - DNA_MARGIN *= mScale; - DNA_WIDTH *= mScale; - DNA_ALL_DAY_HEIGHT *= mScale; - DNA_MIN_SEGMENT_HEIGHT *= mScale; - DNA_SIDE_PADDING *= mScale; - DEFAULT_EDGE_SPACING *= mScale; - DNA_ALL_DAY_WIDTH *= mScale; - TODAY_HIGHLIGHT_WIDTH *= mScale; + val resources: Resources = getContext().getResources() + mShowDetailsInMonth = Utils.getConfigBool(getContext(), R.bool.show_details_in_month) + TEXT_SIZE_EVENT_TITLE = resources.getInteger(R.integer.text_size_event_title) + TEXT_SIZE_MONTH_NUMBER = resources.getInteger(R.integer.text_size_month_number) + SIDE_PADDING_MONTH_NUMBER = resources.getInteger(R.integer.month_day_number_margin) + CONFLICT_COLOR = resources.getColor(R.color.month_dna_conflict_time_color) + EVENT_TEXT_COLOR = resources.getColor(R.color.calendar_event_text_color) + if (mScale !== 1) { + TOP_PADDING_MONTH_NUMBER *= mScale + TOP_PADDING_WEEK_NUMBER *= mScale + SIDE_PADDING_MONTH_NUMBER *= mScale + SIDE_PADDING_WEEK_NUMBER *= mScale + SPACING_WEEK_NUMBER *= mScale + TEXT_SIZE_MONTH_NUMBER *= mScale + TEXT_SIZE_EVENT *= mScale + TEXT_SIZE_EVENT_TITLE *= mScale + TEXT_SIZE_MORE_EVENTS *= mScale + TEXT_SIZE_MONTH_NAME *= mScale + TEXT_SIZE_WEEK_NUM *= mScale + DAY_SEPARATOR_OUTER_WIDTH *= mScale + DAY_SEPARATOR_INNER_WIDTH *= mScale + DAY_SEPARATOR_VERTICAL_LENGTH *= mScale + DAY_SEPARATOR_VERTICAL_LENGTH_PORTRAIT *= mScale + EVENT_X_OFFSET_LANDSCAPE *= mScale + EVENT_Y_OFFSET_LANDSCAPE *= mScale + EVENT_Y_OFFSET_PORTRAIT *= mScale + EVENT_SQUARE_WIDTH *= mScale + EVENT_SQUARE_BORDER *= mScale + EVENT_LINE_PADDING *= mScale + EVENT_BOTTOM_PADDING *= mScale + EVENT_RIGHT_PADDING *= mScale + DNA_MARGIN *= mScale + DNA_WIDTH *= mScale + DNA_ALL_DAY_HEIGHT *= mScale + DNA_MIN_SEGMENT_HEIGHT *= mScale + DNA_SIDE_PADDING *= mScale + DEFAULT_EDGE_SPACING *= mScale + DNA_ALL_DAY_WIDTH *= mScale + TODAY_HIGHLIGHT_WIDTH *= mScale } if (!mShowDetailsInMonth) { - TOP_PADDING_MONTH_NUMBER += DNA_ALL_DAY_HEIGHT + DNA_MARGIN; + TOP_PADDING_MONTH_NUMBER += DNA_ALL_DAY_HEIGHT + DNA_MARGIN } - mInitialized = true; + mInitialized = true } - mPadding = DEFAULT_EDGE_SPACING; - loadColors(getContext()); + mPadding = DEFAULT_EDGE_SPACING + loadColors(getContext()) // TODO modify paint properties depending on isMini - - mMonthNumPaint = new Paint(); - mMonthNumPaint.setFakeBoldText(false); - mMonthNumPaint.setAntiAlias(true); - mMonthNumPaint.setTextSize(TEXT_SIZE_MONTH_NUMBER); - mMonthNumPaint.setColor(mMonthNumColor); - mMonthNumPaint.setStyle(Style.FILL); - mMonthNumPaint.setTextAlign(Align.RIGHT); - mMonthNumPaint.setTypeface(Typeface.DEFAULT); - - mMonthNumAscentHeight = (int) (-mMonthNumPaint.ascent() + 0.5f); - mMonthNumHeight = (int) (mMonthNumPaint.descent() - mMonthNumPaint.ascent() + 0.5f); - - mEventPaint = new TextPaint(); - mEventPaint.setFakeBoldText(true); - mEventPaint.setAntiAlias(true); - mEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE); - mEventPaint.setColor(mMonthEventColor); - - mSolidBackgroundEventPaint = new TextPaint(mEventPaint); - mSolidBackgroundEventPaint.setColor(EVENT_TEXT_COLOR); - mFramedEventPaint = new TextPaint(mSolidBackgroundEventPaint); - - mDeclinedEventPaint = new TextPaint(); - mDeclinedEventPaint.setFakeBoldText(true); - mDeclinedEventPaint.setAntiAlias(true); - mDeclinedEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE); - mDeclinedEventPaint.setColor(mMonthDeclinedEventColor); - - mEventAscentHeight = (int) (-mEventPaint.ascent() + 0.5f); - mEventHeight = (int) (mEventPaint.descent() - mEventPaint.ascent() + 0.5f); - - mEventExtrasPaint = new TextPaint(); - mEventExtrasPaint.setFakeBoldText(false); - mEventExtrasPaint.setAntiAlias(true); - mEventExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER); - mEventExtrasPaint.setTextSize(TEXT_SIZE_EVENT); - mEventExtrasPaint.setColor(mMonthEventExtraColor); - mEventExtrasPaint.setStyle(Style.FILL); - mEventExtrasPaint.setTextAlign(Align.LEFT); - mExtrasHeight = (int)(mEventExtrasPaint.descent() - mEventExtrasPaint.ascent() + 0.5f); - mExtrasAscentHeight = (int)(-mEventExtrasPaint.ascent() + 0.5f); - mExtrasDescent = (int)(mEventExtrasPaint.descent() + 0.5f); - - mEventDeclinedExtrasPaint = new TextPaint(); - mEventDeclinedExtrasPaint.setFakeBoldText(false); - mEventDeclinedExtrasPaint.setAntiAlias(true); - mEventDeclinedExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER); - mEventDeclinedExtrasPaint.setTextSize(TEXT_SIZE_EVENT); - mEventDeclinedExtrasPaint.setColor(mMonthDeclinedExtrasColor); - mEventDeclinedExtrasPaint.setStyle(Style.FILL); - mEventDeclinedExtrasPaint.setTextAlign(Align.LEFT); - - mWeekNumPaint = new Paint(); - mWeekNumPaint.setFakeBoldText(false); - mWeekNumPaint.setAntiAlias(true); - mWeekNumPaint.setTextSize(TEXT_SIZE_WEEK_NUM); - mWeekNumPaint.setColor(mWeekNumColor); - mWeekNumPaint.setStyle(Style.FILL); - mWeekNumPaint.setTextAlign(Align.RIGHT); - - mWeekNumAscentHeight = (int) (-mWeekNumPaint.ascent() + 0.5f); - - mDNAAllDayPaint = new Paint(); - mDNATimePaint = new Paint(); - mDNATimePaint.setColor(mMonthBusyBitsBusyTimeColor); - mDNATimePaint.setStyle(Style.FILL_AND_STROKE); - mDNATimePaint.setStrokeWidth(DNA_WIDTH); - mDNATimePaint.setAntiAlias(false); - mDNAAllDayPaint.setColor(mMonthBusyBitsConflictTimeColor); - mDNAAllDayPaint.setStyle(Style.FILL_AND_STROKE); - mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH); - mDNAAllDayPaint.setAntiAlias(false); - - mEventSquarePaint = new Paint(); - mEventSquarePaint.setStrokeWidth(EVENT_SQUARE_BORDER); - mEventSquarePaint.setAntiAlias(false); - + mMonthNumPaint = Paint() + mMonthNumPaint.setFakeBoldText(false) + mMonthNumPaint.setAntiAlias(true) + mMonthNumPaint.setTextSize(TEXT_SIZE_MONTH_NUMBER) + mMonthNumPaint.setColor(mMonthNumColor) + mMonthNumPaint.setStyle(Style.FILL) + mMonthNumPaint.setTextAlign(Align.RIGHT) + mMonthNumPaint.setTypeface(Typeface.DEFAULT) + mMonthNumAscentHeight = (-mMonthNumPaint.ascent() + 0.5f) + mMonthNumHeight = (mMonthNumPaint.descent() - mMonthNumPaint.ascent() + 0.5f) + mEventPaint = TextPaint() + mEventPaint.setFakeBoldText(true) + mEventPaint.setAntiAlias(true) + mEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE) + mEventPaint.setColor(mMonthEventColor) + mSolidBackgroundEventPaint = TextPaint(mEventPaint) + mSolidBackgroundEventPaint.setColor(EVENT_TEXT_COLOR) + mFramedEventPaint = TextPaint(mSolidBackgroundEventPaint) + mDeclinedEventPaint = TextPaint() + mDeclinedEventPaint.setFakeBoldText(true) + mDeclinedEventPaint.setAntiAlias(true) + mDeclinedEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE) + mDeclinedEventPaint.setColor(mMonthDeclinedEventColor) + mEventAscentHeight = (-mEventPaint.ascent() + 0.5f) + mEventHeight = (mEventPaint.descent() - mEventPaint.ascent() + 0.5f) + mEventExtrasPaint = TextPaint() + mEventExtrasPaint.setFakeBoldText(false) + mEventExtrasPaint.setAntiAlias(true) + mEventExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER) + mEventExtrasPaint.setTextSize(TEXT_SIZE_EVENT) + mEventExtrasPaint.setColor(mMonthEventExtraColor) + mEventExtrasPaint.setStyle(Style.FILL) + mEventExtrasPaint.setTextAlign(Align.LEFT) + mExtrasHeight = (mEventExtrasPaint.descent() - mEventExtrasPaint.ascent() + 0.5f) + mExtrasAscentHeight = (-mEventExtrasPaint.ascent() + 0.5f) + mExtrasDescent = (mEventExtrasPaint.descent() + 0.5f) + mEventDeclinedExtrasPaint = TextPaint() + mEventDeclinedExtrasPaint.setFakeBoldText(false) + mEventDeclinedExtrasPaint.setAntiAlias(true) + mEventDeclinedExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER) + mEventDeclinedExtrasPaint.setTextSize(TEXT_SIZE_EVENT) + mEventDeclinedExtrasPaint.setColor(mMonthDeclinedExtrasColor) + mEventDeclinedExtrasPaint.setStyle(Style.FILL) + mEventDeclinedExtrasPaint.setTextAlign(Align.LEFT) + mWeekNumPaint = Paint() + mWeekNumPaint.setFakeBoldText(false) + mWeekNumPaint.setAntiAlias(true) + mWeekNumPaint.setTextSize(TEXT_SIZE_WEEK_NUM) + mWeekNumPaint.setColor(mWeekNumColor) + mWeekNumPaint.setStyle(Style.FILL) + mWeekNumPaint.setTextAlign(Align.RIGHT) + mWeekNumAscentHeight = (-mWeekNumPaint.ascent() + 0.5f) + mDNAAllDayPaint = Paint() + mDNATimePaint = Paint() + mDNATimePaint.setColor(mMonthBusyBitsBusyTimeColor) + mDNATimePaint.setStyle(Style.FILL_AND_STROKE) + mDNATimePaint.setStrokeWidth(DNA_WIDTH) + mDNATimePaint.setAntiAlias(false) + mDNAAllDayPaint.setColor(mMonthBusyBitsConflictTimeColor) + mDNAAllDayPaint.setStyle(Style.FILL_AND_STROKE) + mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH) + mDNAAllDayPaint.setAntiAlias(false) + mEventSquarePaint = Paint() + mEventSquarePaint.setStrokeWidth(EVENT_SQUARE_BORDER) + mEventSquarePaint.setAntiAlias(false) if (DEBUG_LAYOUT) { - Log.d("EXTRA", "mScale=" + mScale); + Log.d("EXTRA", "mScale=$mScale") Log.d("EXTRA", "mMonthNumPaint ascent=" + mMonthNumPaint.ascent() - + " descent=" + mMonthNumPaint.descent() + " int height=" + mMonthNumHeight); + .toString() + " descent=" + mMonthNumPaint.descent().toString() + " int height=" + mMonthNumHeight) Log.d("EXTRA", "mEventPaint ascent=" + mEventPaint.ascent() - + " descent=" + mEventPaint.descent() + " int height=" + mEventHeight - + " int ascent=" + mEventAscentHeight); + .toString() + " descent=" + mEventPaint.descent().toString() + " int height=" + mEventHeight + .toString() + " int ascent=" + mEventAscentHeight) Log.d("EXTRA", "mEventExtrasPaint ascent=" + mEventExtrasPaint.ascent() - + " descent=" + mEventExtrasPaint.descent() + " int height=" + mExtrasHeight); + .toString() + " descent=" + mEventExtrasPaint.descent().toString() + " int height=" + mExtrasHeight) Log.d("EXTRA", "mWeekNumPaint ascent=" + mWeekNumPaint.ascent() - + " descent=" + mWeekNumPaint.descent()); + .toString() + " descent=" + mWeekNumPaint.descent()) } } @Override - public void setWeekParams(HashMap params, String tz) { - super.setWeekParams(params, tz); - + fun setWeekParams(params: HashMap, tz: String) { + super.setWeekParams(params, tz) if (params.containsKey(VIEW_PARAMS_ORIENTATION)) { - mOrientation = params.get(VIEW_PARAMS_ORIENTATION); + mOrientation = params.get(VIEW_PARAMS_ORIENTATION) } - - updateToday(tz); - mNumCells = mNumDays + 1; - + updateToday(tz) + mNumCells = mNumDays + 1 if (params.containsKey(VIEW_PARAMS_ANIMATE_TODAY) && mHasToday) { - synchronized (mAnimatorListener) { + synchronized(mAnimatorListener) { if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners(); - mTodayAnimator.cancel(); + mTodayAnimator.removeAllListeners() + mTodayAnimator.cancel() } mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha", - Math.max(mAnimateTodayAlpha, 80), 255); - mTodayAnimator.setDuration(150); - mAnimatorListener.setAnimator(mTodayAnimator); - mAnimatorListener.setFadingIn(true); - mTodayAnimator.addListener(mAnimatorListener); - mAnimateToday = true; - mTodayAnimator.start(); + Math.max(mAnimateTodayAlpha, 80), 255) + mTodayAnimator.setDuration(150) + mAnimatorListener.setAnimator(mTodayAnimator) + mAnimatorListener.setFadingIn(true) + mTodayAnimator.addListener(mAnimatorListener) + mAnimateToday = true + mTodayAnimator.start() } } } @@ -518,287 +395,277 @@ public class MonthWeekEventsView extends SimpleWeekView { /** * @param tz */ - public boolean updateToday(String tz) { - mToday.timezone = tz; - mToday.setToNow(); - mToday.normalize(true); - int julianToday = Time.getJulianDay(mToday.toMillis(false), mToday.gmtoff); + fun updateToday(tz: String): Boolean { + mToday.timezone = tz + mToday.setToNow() + mToday.normalize(true) + val julianToday: Int = Time.getJulianDay(mToday.toMillis(false), mToday.gmtoff) if (julianToday >= mFirstJulianDay && julianToday < mFirstJulianDay + mNumDays) { - mHasToday = true; - mTodayIndex = julianToday - mFirstJulianDay; + mHasToday = true + mTodayIndex = julianToday - mFirstJulianDay } else { - mHasToday = false; - mTodayIndex = -1; + mHasToday = false + mTodayIndex = -1 } - return mHasToday; + return mHasToday } - public void setAnimateTodayAlpha(int alpha) { - mAnimateTodayAlpha = alpha; - invalidate(); + fun setAnimateTodayAlpha(alpha: Int) { + mAnimateTodayAlpha = alpha + invalidate() } @Override - protected void onDraw(Canvas canvas) { - drawBackground(canvas); - drawWeekNums(canvas); - drawDaySeparators(canvas); + protected fun onDraw(canvas: Canvas) { + drawBackground(canvas) + drawWeekNums(canvas) + drawDaySeparators(canvas) if (mHasToday && mAnimateToday) { - drawToday(canvas); + drawToday(canvas) } if (mShowDetailsInMonth) { - drawEvents(canvas); + drawEvents(canvas) } else { if (mDna == null && mUnsortedEvents != null) { - createDna(mUnsortedEvents); + createDna(mUnsortedEvents) } - drawDNA(canvas); + drawDNA(canvas) } - drawClick(canvas); + drawClick(canvas) } - protected void drawToday(Canvas canvas) { - r.top = DAY_SEPARATOR_INNER_WIDTH + (TODAY_HIGHLIGHT_WIDTH / 2); - r.bottom = mHeight - (int) Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f); - p.setStyle(Style.STROKE); - p.setStrokeWidth(TODAY_HIGHLIGHT_WIDTH); - r.left = computeDayLeftPosition(mTodayIndex) + (TODAY_HIGHLIGHT_WIDTH / 2); - r.right = computeDayLeftPosition(mTodayIndex + 1) - - (int) Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f); - p.setColor(mTodayAnimateColor | (mAnimateTodayAlpha << 24)); - canvas.drawRect(r, p); - p.setStyle(Style.FILL); + protected fun drawToday(canvas: Canvas) { + r.top = DAY_SEPARATOR_INNER_WIDTH + TODAY_HIGHLIGHT_WIDTH / 2 + r.bottom = mHeight - Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f) as Int + p.setStyle(Style.STROKE) + p.setStrokeWidth(TODAY_HIGHLIGHT_WIDTH) + r.left = computeDayLeftPosition(mTodayIndex) + TODAY_HIGHLIGHT_WIDTH / 2 + r.right = (computeDayLeftPosition(mTodayIndex + 1) + - Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f) as Int) + p.setColor(mTodayAnimateColor or (mAnimateTodayAlpha shl 24)) + canvas.drawRect(r, p) + p.setStyle(Style.FILL) } // TODO move into SimpleWeekView // Computes the x position for the left side of the given day - private int computeDayLeftPosition(int day) { - int effectiveWidth = mWidth; - int x = 0; - int xOffset = 0; + private fun computeDayLeftPosition(day: Int): Int { + var effectiveWidth: Int = mWidth + var x = 0 + var xOffset = 0 if (mShowWeekNum) { - xOffset = SPACING_WEEK_NUMBER + mPadding; - effectiveWidth -= xOffset; + xOffset = SPACING_WEEK_NUMBER + mPadding + effectiveWidth -= xOffset } - x = day * effectiveWidth / mNumDays + xOffset; - return x; + x = day * effectiveWidth / mNumDays + xOffset + return x } @Override - protected void drawDaySeparators(Canvas canvas) { - float lines[] = new float[8 * 4]; - int count = 6 * 4; - int wkNumOffset = 0; - int i = 0; + protected fun drawDaySeparators(canvas: Canvas) { + val lines = FloatArray(8 * 4) + var count = 6 * 4 + var wkNumOffset = 0 + var i = 0 if (mShowWeekNum) { // This adds the first line separating the week number - int xOffset = SPACING_WEEK_NUMBER + mPadding; - count += 4; - lines[i++] = xOffset; - lines[i++] = 0; - lines[i++] = xOffset; - lines[i++] = mHeight; - wkNumOffset++; - } - count += 4; - lines[i++] = 0; - lines[i++] = 0; - lines[i++] = mWidth; - lines[i++] = 0; - int y0 = 0; - int y1 = mHeight; - + val xOffset: Int = SPACING_WEEK_NUMBER + mPadding + count += 4 + lines[i++] = xOffset.toFloat() + lines[i++] = 0 + lines[i++] = xOffset.toFloat() + lines[i++] = mHeight + wkNumOffset++ + } + count += 4 + lines[i++] = 0 + lines[i++] = 0 + lines[i++] = mWidth + lines[i++] = 0 + val y0 = 0 + val y1: Int = mHeight while (i < count) { - int x = computeDayLeftPosition(i / 4 - wkNumOffset); - lines[i++] = x; - lines[i++] = y0; - lines[i++] = x; - lines[i++] = y1; - } - p.setColor(mDaySeparatorInnerColor); - p.setStrokeWidth(DAY_SEPARATOR_INNER_WIDTH); - canvas.drawLines(lines, 0, count, p); + val x = computeDayLeftPosition(i / 4 - wkNumOffset) + lines[i++] = x.toFloat() + lines[i++] = y0.toFloat() + lines[i++] = x.toFloat() + lines[i++] = y1.toFloat() + } + p.setColor(mDaySeparatorInnerColor) + p.setStrokeWidth(DAY_SEPARATOR_INNER_WIDTH) + canvas.drawLines(lines, 0, count, p) } @Override - protected void drawBackground(Canvas canvas) { - int i = 0; - int offset = 0; - r.top = DAY_SEPARATOR_INNER_WIDTH; - r.bottom = mHeight; + protected fun drawBackground(canvas: Canvas) { + var i = 0 + var offset = 0 + r.top = DAY_SEPARATOR_INNER_WIDTH + r.bottom = mHeight if (mShowWeekNum) { - i++; - offset++; - } - if (!mOddMonth[i]) { - while (++i < mOddMonth.length && !mOddMonth[i]) - ; - r.right = computeDayLeftPosition(i - offset); - r.left = 0; - p.setColor(mMonthBGOtherColor); - canvas.drawRect(r, p); + i++ + offset++ + } + if (!mOddMonth.get(i)) { + while (++i < mOddMonth.length && !mOddMonth.get(i)); + r.right = computeDayLeftPosition(i - offset) + r.left = 0 + p.setColor(mMonthBGOtherColor) + canvas.drawRect(r, p) // compute left edge for i, set up r, draw - } else if (!mOddMonth[(i = mOddMonth.length - 1)]) { - while (--i >= offset && !mOddMonth[i]) - ; - i++; + } else if (!mOddMonth.get(mOddMonth.length - 1.also { i = it })) { + while (--i >= offset && !mOddMonth.get(i)); + i++ // compute left edge for i, set up r, draw - r.right = mWidth; - r.left = computeDayLeftPosition(i - offset); - p.setColor(mMonthBGOtherColor); - canvas.drawRect(r, p); + r.right = mWidth + r.left = computeDayLeftPosition(i - offset) + p.setColor(mMonthBGOtherColor) + canvas.drawRect(r, p) } if (mHasToday) { - p.setColor(mMonthBGTodayColor); - r.left = computeDayLeftPosition(mTodayIndex); - r.right = computeDayLeftPosition(mTodayIndex + 1); - canvas.drawRect(r, p); + p.setColor(mMonthBGTodayColor) + r.left = computeDayLeftPosition(mTodayIndex) + r.right = computeDayLeftPosition(mTodayIndex + 1) + canvas.drawRect(r, p) } } // Draw the "clicked" color on the tapped day - private void drawClick(Canvas canvas) { + private fun drawClick(canvas: Canvas) { if (mClickedDayIndex != -1) { - int alpha = p.getAlpha(); - p.setColor(mClickedDayColor); - p.setAlpha(mClickedAlpha); - r.left = computeDayLeftPosition(mClickedDayIndex); - r.right = computeDayLeftPosition(mClickedDayIndex + 1); - r.top = DAY_SEPARATOR_INNER_WIDTH; - r.bottom = mHeight; - canvas.drawRect(r, p); - p.setAlpha(alpha); + val alpha: Int = p.getAlpha() + p.setColor(mClickedDayColor) + p.setAlpha(mClickedAlpha) + r.left = computeDayLeftPosition(mClickedDayIndex) + r.right = computeDayLeftPosition(mClickedDayIndex + 1) + r.top = DAY_SEPARATOR_INNER_WIDTH + r.bottom = mHeight + canvas.drawRect(r, p) + p.setAlpha(alpha) } } @Override - protected void drawWeekNums(Canvas canvas) { - int y; - - int i = 0; - int offset = -1; - int todayIndex = mTodayIndex; - int x = 0; - int numCount = mNumDays; + protected fun drawWeekNums(canvas: Canvas) { + var y: Int + var i = 0 + var offset = -1 + var todayIndex = mTodayIndex + var x = 0 + var numCount: Int = mNumDays if (mShowWeekNum) { - x = SIDE_PADDING_WEEK_NUMBER + mPadding; - y = mWeekNumAscentHeight + TOP_PADDING_WEEK_NUMBER; - canvas.drawText(mDayNumbers[0], x, y, mWeekNumPaint); - numCount++; - i++; - todayIndex++; - offset++; - - } - - y = mMonthNumAscentHeight + TOP_PADDING_MONTH_NUMBER; - - boolean isFocusMonth = mFocusDay[i]; - boolean isBold = false; - mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); - for (; i < numCount; i++) { + x = SIDE_PADDING_WEEK_NUMBER + mPadding + y = mWeekNumAscentHeight + TOP_PADDING_WEEK_NUMBER + canvas.drawText(mDayNumbers.get(0), x, y, mWeekNumPaint) + numCount++ + i++ + todayIndex++ + offset++ + } + y = mMonthNumAscentHeight + TOP_PADDING_MONTH_NUMBER + var isFocusMonth: Boolean = mFocusDay.get(i) + var isBold = false + mMonthNumPaint.setColor(if (isFocusMonth) mMonthNumColor else mMonthNumOtherColor) + while (i < numCount) { if (mHasToday && todayIndex == i) { - mMonthNumPaint.setColor(mMonthNumTodayColor); - mMonthNumPaint.setFakeBoldText(isBold = true); + mMonthNumPaint.setColor(mMonthNumTodayColor) + mMonthNumPaint.setFakeBoldText(true.also { isBold = it }) if (i + 1 < numCount) { // Make sure the color will be set back on the next // iteration - isFocusMonth = !mFocusDay[i + 1]; + isFocusMonth = !mFocusDay.get(i + 1) } - } else if (mFocusDay[i] != isFocusMonth) { - isFocusMonth = mFocusDay[i]; - mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); + } else if (mFocusDay.get(i) !== isFocusMonth) { + isFocusMonth = mFocusDay.get(i) + mMonthNumPaint.setColor(if (isFocusMonth) mMonthNumColor else mMonthNumOtherColor) } - x = computeDayLeftPosition(i - offset) - (SIDE_PADDING_MONTH_NUMBER); - canvas.drawText(mDayNumbers[i], x, y, mMonthNumPaint); + x = computeDayLeftPosition(i - offset) - SIDE_PADDING_MONTH_NUMBER + canvas.drawText(mDayNumbers.get(i), x, y, mMonthNumPaint) if (isBold) { - mMonthNumPaint.setFakeBoldText(isBold = false); + mMonthNumPaint.setFakeBoldText(false.also { isBold = it }) } + i++ } } - protected void drawEvents(Canvas canvas) { + protected fun drawEvents(canvas: Canvas) { if (mEvents == null) { - return; + return } - - int day = -1; - for (ArrayList eventDay : mEvents) { - day++; - if (eventDay == null || eventDay.size() == 0) { - continue; + var day = -1 + for (eventDay in mEvents!!) { + day++ + if (eventDay == null || eventDay.size() === 0) { + continue } - int ySquare; - int xSquare = computeDayLeftPosition(day) + SIDE_PADDING_MONTH_NUMBER + 1; - int rightEdge = computeDayLeftPosition(day + 1); - + var ySquare: Int + val xSquare = computeDayLeftPosition(day) + SIDE_PADDING_MONTH_NUMBER + 1 + var rightEdge = computeDayLeftPosition(day + 1) if (mOrientation == Configuration.ORIENTATION_PORTRAIT) { - ySquare = EVENT_Y_OFFSET_PORTRAIT + mMonthNumHeight + TOP_PADDING_MONTH_NUMBER; - rightEdge -= SIDE_PADDING_MONTH_NUMBER + 1; + ySquare = EVENT_Y_OFFSET_PORTRAIT + mMonthNumHeight + TOP_PADDING_MONTH_NUMBER + rightEdge -= SIDE_PADDING_MONTH_NUMBER + 1 } else { - ySquare = EVENT_Y_OFFSET_LANDSCAPE; - rightEdge -= EVENT_X_OFFSET_LANDSCAPE; + ySquare = EVENT_Y_OFFSET_LANDSCAPE + rightEdge -= EVENT_X_OFFSET_LANDSCAPE } // Determine if everything will fit when time ranges are shown. - boolean showTimes = true; - Iterator iter = eventDay.iterator(); - int yTest = ySquare; + var showTimes = true + var iter: Iterator = eventDay.iterator() + var yTest = ySquare while (iter.hasNext()) { - Event event = iter.next(); - int newY = drawEvent(canvas, event, xSquare, yTest, rightEdge, iter.hasNext(), - showTimes, /*doDraw*/ false); + val event: Event = iter.next() + val newY = drawEvent(canvas, event, xSquare, yTest, rightEdge, iter.hasNext(), + showTimes, /*doDraw*/false) if (newY == yTest) { - showTimes = false; - break; + showTimes = false + break } - yTest = newY; + yTest = newY } - - int eventCount = 0; - iter = eventDay.iterator(); + var eventCount = 0 + iter = eventDay.iterator() while (iter.hasNext()) { - Event event = iter.next(); - int newY = drawEvent(canvas, event, xSquare, ySquare, rightEdge, iter.hasNext(), - showTimes, /*doDraw*/ true); + val event: Event = iter.next() + val newY = drawEvent(canvas, event, xSquare, ySquare, rightEdge, iter.hasNext(), + showTimes, /*doDraw*/true) if (newY == ySquare) { - break; + break } - eventCount++; - ySquare = newY; + eventCount++ + ySquare = newY } - - int remaining = eventDay.size() - eventCount; + val remaining: Int = eventDay.size() - eventCount if (remaining > 0) { - drawMoreEvents(canvas, remaining, xSquare); + drawMoreEvents(canvas, remaining, xSquare) } } } - protected int addChipOutline(FloatRef lines, int count, int x, int y) { - lines.ensureSize(count + 16); + protected fun addChipOutline(lines: FloatRef, count: Int, x: Int, y: Int): Int { + var count = count + lines.ensureSize(count + 16) // top of box - lines.array[count++] = x; - lines.array[count++] = y; - lines.array[count++] = x + EVENT_SQUARE_WIDTH; - lines.array[count++] = y; + lines.array[count++] = x.toFloat() + lines.array[count++] = y.toFloat() + lines.array[count++] = (x + EVENT_SQUARE_WIDTH).toFloat() + lines.array[count++] = y.toFloat() // right side of box - lines.array[count++] = x + EVENT_SQUARE_WIDTH; - lines.array[count++] = y; - lines.array[count++] = x + EVENT_SQUARE_WIDTH; - lines.array[count++] = y + EVENT_SQUARE_WIDTH; + lines.array[count++] = (x + EVENT_SQUARE_WIDTH).toFloat() + lines.array[count++] = y.toFloat() + lines.array[count++] = (x + EVENT_SQUARE_WIDTH).toFloat() + lines.array[count++] = (y + EVENT_SQUARE_WIDTH).toFloat() // left side of box - lines.array[count++] = x; - lines.array[count++] = y; - lines.array[count++] = x; - lines.array[count++] = y + EVENT_SQUARE_WIDTH + 1; + lines.array[count++] = x.toFloat() + lines.array[count++] = y.toFloat() + lines.array[count++] = x.toFloat() + lines.array[count++] = (y + EVENT_SQUARE_WIDTH + 1).toFloat() // bottom of box - lines.array[count++] = x; - lines.array[count++] = y + EVENT_SQUARE_WIDTH; - lines.array[count++] = x + EVENT_SQUARE_WIDTH + 1; - lines.array[count++] = y + EVENT_SQUARE_WIDTH; - - return count; + lines.array[count++] = x.toFloat() + lines.array[count++] = (y + EVENT_SQUARE_WIDTH).toFloat() + lines.array[count++] = (x + EVENT_SQUARE_WIDTH + 1).toFloat() + lines.array[count++] = (y + EVENT_SQUARE_WIDTH).toFloat() + return count } /** @@ -814,13 +681,13 @@ public class MonthWeekEventsView extends SimpleWeekView { * @param rightEdge the rightmost point we're allowed to draw on (exclusive) * @param moreEvents indicates whether additional events will follow this one * @param showTimes if set, a second line with a time range will be displayed for non-all-day - * events + * events * @param doDraw if set, do the actual drawing; otherwise this just computes the height - * and returns + * and returns * @return the y for the next event or the original y if it won't fit */ - protected int drawEvent(Canvas canvas, Event event, int x, int y, int rightEdge, - boolean moreEvents, boolean showTimes, boolean doDraw) { + protected fun drawEvent(canvas: Canvas, event: Event, x: Int, y: Int, rightEdge: Int, + moreEvents: Boolean, showTimes: Boolean, doDraw: Boolean): Int { /* * Vertical layout: * (top of box) @@ -837,126 +704,119 @@ public class MonthWeekEventsView extends SimpleWeekView { * f. EVENT_BOTTOM_PADDING (overlaps EVENT_LINE_PADDING) * (bottom of box) */ - final int BORDER_SPACE = EVENT_SQUARE_BORDER + 1; // want a 1-pixel gap inside border - final int STROKE_WIDTH_ADJ = EVENT_SQUARE_BORDER / 2; // adjust bounds for stroke width - boolean allDay = event.allDay; - int eventRequiredSpace = mEventHeight; + var y = y + val BORDER_SPACE = EVENT_SQUARE_BORDER + 1 // want a 1-pixel gap inside border + val STROKE_WIDTH_ADJ = EVENT_SQUARE_BORDER / 2 // adjust bounds for stroke width + val allDay: Boolean = event.allDay + var eventRequiredSpace = mEventHeight if (allDay) { // Add a few pixels for the box we draw around all-day events. - eventRequiredSpace += BORDER_SPACE * 2; + eventRequiredSpace += BORDER_SPACE * 2 } else if (showTimes) { // Need room for the "1pm - 2pm" line. - eventRequiredSpace += mExtrasHeight; + eventRequiredSpace += mExtrasHeight } - int reservedSpace = EVENT_BOTTOM_PADDING; // leave a bit of room at the bottom + var reservedSpace = EVENT_BOTTOM_PADDING // leave a bit of room at the bottom if (moreEvents) { // More events follow. Leave a bit of space between events. - eventRequiredSpace += EVENT_LINE_PADDING; + eventRequiredSpace += EVENT_LINE_PADDING // Make sure we have room for the "+ more" line. (The "+ more" line is expected // to be <= the height of an event line, so we won't show "+1" when we could be // showing the event.) - reservedSpace += mExtrasHeight; + reservedSpace += mExtrasHeight } - if (y + eventRequiredSpace + reservedSpace > mHeight) { // Not enough space, return original y - return y; + return y } else if (!doDraw) { - return y + eventRequiredSpace; + return y + eventRequiredSpace } - - boolean isDeclined = event.selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED; - int color = event.color; + val isDeclined = event.selfAttendeeStatus === Attendees.ATTENDEE_STATUS_DECLINED + var color: Int = event.color if (isDeclined) { - color = Utils.getDeclinedColorFromColor(color); + color = Utils.getDeclinedColorFromColor(color) } - - int textX, textY, textRightEdge; - + val textX: Int + var textY: Int + val textRightEdge: Int if (allDay) { // We shift the render offset "inward", because drawRect with a stroke width greater // than 1 draws outside the specified bounds. (We don't adjust the left edge, since // we want to match the existing appearance of the "event square".) - r.left = x; - r.right = rightEdge - STROKE_WIDTH_ADJ; - r.top = y + STROKE_WIDTH_ADJ; - r.bottom = y + mEventHeight + BORDER_SPACE * 2 - STROKE_WIDTH_ADJ; - textX = x + BORDER_SPACE; - textY = y + mEventAscentHeight + BORDER_SPACE; - textRightEdge = rightEdge - BORDER_SPACE; + r.left = x + r.right = rightEdge - STROKE_WIDTH_ADJ + r.top = y + STROKE_WIDTH_ADJ + r.bottom = y + mEventHeight + BORDER_SPACE * 2 - STROKE_WIDTH_ADJ + textX = x + BORDER_SPACE + textY = y + mEventAscentHeight + BORDER_SPACE + textRightEdge = rightEdge - BORDER_SPACE } else { - r.left = x; - r.right = x + EVENT_SQUARE_WIDTH; - r.bottom = y + mEventAscentHeight; - r.top = r.bottom - EVENT_SQUARE_WIDTH; - textX = x + EVENT_SQUARE_WIDTH + EVENT_RIGHT_PADDING; - textY = y + mEventAscentHeight; - textRightEdge = rightEdge; - } - - Style boxStyle = Style.STROKE; - boolean solidBackground = false; - if (event.selfAttendeeStatus != Attendees.ATTENDEE_STATUS_INVITED) { - boxStyle = Style.FILL_AND_STROKE; + r.left = x + r.right = x + EVENT_SQUARE_WIDTH + r.bottom = y + mEventAscentHeight + r.top = r.bottom - EVENT_SQUARE_WIDTH + textX = x + EVENT_SQUARE_WIDTH + EVENT_RIGHT_PADDING + textY = y + mEventAscentHeight + textRightEdge = rightEdge + } + var boxStyle: Style = Style.STROKE + var solidBackground = false + if (event.selfAttendeeStatus !== Attendees.ATTENDEE_STATUS_INVITED) { + boxStyle = Style.FILL_AND_STROKE if (allDay) { - solidBackground = true; + solidBackground = true } } - mEventSquarePaint.setStyle(boxStyle); - mEventSquarePaint.setColor(color); - canvas.drawRect(r, mEventSquarePaint); - - float avail = textRightEdge - textX; - CharSequence text = TextUtils.ellipsize( - event.title, mEventPaint, avail, TextUtils.TruncateAt.END); - Paint textPaint; - if (solidBackground) { + mEventSquarePaint.setStyle(boxStyle) + mEventSquarePaint.setColor(color) + canvas.drawRect(r, mEventSquarePaint) + val avail = (textRightEdge - textX).toFloat() + var text: CharSequence = TextUtils.ellipsize( + event.title, mEventPaint, avail, TextUtils.TruncateAt.END) + val textPaint: Paint + textPaint = if (solidBackground) { // Text color needs to contrast with solid background. - textPaint = mSolidBackgroundEventPaint; + mSolidBackgroundEventPaint } else if (isDeclined) { // Use "declined event" color. - textPaint = mDeclinedEventPaint; + mDeclinedEventPaint } else if (allDay) { // Text inside frame is same color as frame. - mFramedEventPaint.setColor(color); - textPaint = mFramedEventPaint; + mFramedEventPaint.setColor(color) + mFramedEventPaint } else { // Use generic event text color. - textPaint = mEventPaint; + mEventPaint } - canvas.drawText(text.toString(), textX, textY, textPaint); - y += mEventHeight; + canvas.drawText(text.toString(), textX, textY, textPaint) + y += mEventHeight if (allDay) { - y += BORDER_SPACE * 2; + y += BORDER_SPACE * 2 } - if (showTimes && !allDay) { // show start/end time, e.g. "1pm - 2pm" - textY = y + mExtrasAscentHeight; - mStringBuilder.setLength(0); + textY = y + mExtrasAscentHeight + mStringBuilder.setLength(0) text = DateUtils.formatDateRange(getContext(), mFormatter, event.startMillis, - event.endMillis, DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL, - Utils.getTimeZone(getContext(), null)).toString(); - text = TextUtils.ellipsize(text, mEventExtrasPaint, avail, TextUtils.TruncateAt.END); - canvas.drawText(text.toString(), textX, textY, isDeclined ? mEventDeclinedExtrasPaint - : mEventExtrasPaint); - y += mExtrasHeight; - } - - y += EVENT_LINE_PADDING; - - return y; + event.endMillis, DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_ABBREV_ALL, + Utils.getTimeZone(getContext(), null)).toString() + text = TextUtils.ellipsize(text, mEventExtrasPaint, avail, TextUtils.TruncateAt.END) + canvas.drawText(text.toString(), textX, textY, if (isDeclined) mEventDeclinedExtrasPaint else mEventExtrasPaint) + y += mExtrasHeight + } + y += EVENT_LINE_PADDING + return y } - protected void drawMoreEvents(Canvas canvas, int remainingEvents, int x) { - int y = mHeight - (mExtrasDescent + EVENT_BOTTOM_PADDING); - String text = getContext().getResources().getQuantityString( - R.plurals.month_more_events, remainingEvents); - mEventExtrasPaint.setAntiAlias(true); - mEventExtrasPaint.setFakeBoldText(true); - canvas.drawText(String.format(text, remainingEvents), x, y, mEventExtrasPaint); - mEventExtrasPaint.setFakeBoldText(false); + protected fun drawMoreEvents(canvas: Canvas, remainingEvents: Int, x: Int) { + val y: Int = mHeight - (mExtrasDescent + EVENT_BOTTOM_PADDING) + val text: String = getContext().getResources().getQuantityString( + R.plurals.month_more_events, remainingEvents) + mEventExtrasPaint.setAntiAlias(true) + mEventExtrasPaint.setFakeBoldText(true) + canvas.drawText(String.format(text, remainingEvents), x, y, mEventExtrasPaint) + mEventExtrasPaint.setFakeBoldText(false) } /** @@ -966,36 +826,35 @@ public class MonthWeekEventsView extends SimpleWeekView { * * @param canvas */ - protected void drawDNA(Canvas canvas) { + protected fun drawDNA(canvas: Canvas) { // Draw event and conflict times if (mDna != null) { - for (Utils.DNAStrand strand : mDna.values()) { - if (strand.color == CONFLICT_COLOR || strand.points == null - || strand.points.length == 0) { - continue; + for (strand in mDna.values()) { + if (strand.color === CONFLICT_COLOR || strand.points == null || strand.points.length === 0) { + continue } - mDNATimePaint.setColor(strand.color); - canvas.drawLines(strand.points, mDNATimePaint); + mDNATimePaint.setColor(strand.color) + canvas.drawLines(strand.points, mDNATimePaint) } // Draw black last to make sure it's on top - Utils.DNAStrand strand = mDna.get(CONFLICT_COLOR); - if (strand != null && strand.points != null && strand.points.length != 0) { - mDNATimePaint.setColor(strand.color); - canvas.drawLines(strand.points, mDNATimePaint); + val strand: Utils.DNAStrand = mDna.get(CONFLICT_COLOR) + if (strand != null && strand.points != null && strand.points.length !== 0) { + mDNATimePaint.setColor(strand.color) + canvas.drawLines(strand.points, mDNATimePaint) } if (mDayXs == null) { - return; + return } - int numDays = mDayXs.length; - int xOffset = (DNA_ALL_DAY_WIDTH - DNA_WIDTH) / 2; - if (strand != null && strand.allDays != null && strand.allDays.length == numDays) { - for (int i = 0; i < numDays; i++) { + val numDays = mDayXs!!.size + val xOffset = (DNA_ALL_DAY_WIDTH - DNA_WIDTH) / 2 + if (strand != null && strand.allDays != null && strand.allDays.length === numDays) { + for (i in 0 until numDays) { // this adds at most 7 draws. We could sort it by color and // build an array instead but this is easier. - if (strand.allDays[i] != 0) { - mDNAAllDayPaint.setColor(strand.allDays[i]); - canvas.drawLine(mDayXs[i] + xOffset, DNA_MARGIN, mDayXs[i] + xOffset, - DNA_MARGIN + DNA_ALL_DAY_HEIGHT, mDNAAllDayPaint); + if (strand.allDays.get(i) !== 0) { + mDNAAllDayPaint.setColor(strand.allDays.get(i)) + canvas.drawLine(mDayXs!![i] + xOffset, DNA_MARGIN, mDayXs!![i] + xOffset, + DNA_MARGIN + DNA_ALL_DAY_HEIGHT, mDNAAllDayPaint) } } } @@ -1003,108 +862,156 @@ public class MonthWeekEventsView extends SimpleWeekView { } @Override - protected void updateSelectionPositions() { + protected fun updateSelectionPositions() { if (mHasSelectedDay) { - int selectedPosition = mSelectedDay - mWeekStart; + var selectedPosition: Int = mSelectedDay - mWeekStart if (selectedPosition < 0) { - selectedPosition += 7; + selectedPosition += 7 } - int effectiveWidth = mWidth - mPadding * 2; - effectiveWidth -= SPACING_WEEK_NUMBER; - mSelectedLeft = selectedPosition * effectiveWidth / mNumDays + mPadding; - mSelectedRight = (selectedPosition + 1) * effectiveWidth / mNumDays + mPadding; - mSelectedLeft += SPACING_WEEK_NUMBER; - mSelectedRight += SPACING_WEEK_NUMBER; + var effectiveWidth: Int = mWidth - mPadding * 2 + effectiveWidth -= SPACING_WEEK_NUMBER + mSelectedLeft = selectedPosition * effectiveWidth / mNumDays + mPadding + mSelectedRight = (selectedPosition + 1) * effectiveWidth / mNumDays + mPadding + mSelectedLeft += SPACING_WEEK_NUMBER + mSelectedRight += SPACING_WEEK_NUMBER } } - public int getDayIndexFromLocation(float x) { - int dayStart = mShowWeekNum ? SPACING_WEEK_NUMBER + mPadding : mPadding; - if (x < dayStart || x > mWidth - mPadding) { - return -1; - } + fun getDayIndexFromLocation(x: Float): Int { + val dayStart: Int = if (mShowWeekNum) SPACING_WEEK_NUMBER + mPadding else mPadding + return if (x < dayStart || x > mWidth - mPadding) { + -1 + } else ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)) // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels - return ((int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding))); } @Override - public Time getDayFromLocation(float x) { - int dayPosition = getDayIndexFromLocation(x); + fun getDayFromLocation(x: Float): Time? { + val dayPosition = getDayIndexFromLocation(x) if (dayPosition == -1) { - return null; + return null } - int day = mFirstJulianDay + dayPosition; - - Time time = new Time(mTimeZone); - if (mWeek == 0) { + var day: Int = mFirstJulianDay + dayPosition + val time = Time(mTimeZone) + if (mWeek === 0) { // This week is weird... if (day < Time.EPOCH_JULIAN_DAY) { - day++; + day++ } else if (day == Time.EPOCH_JULIAN_DAY) { - time.set(1, 0, 1970); - time.normalize(true); - return time; + time.set(1, 0, 1970) + time.normalize(true) + return time } } - - time.setJulianDay(day); - return time; + time.setJulianDay(day) + return time } @Override - public boolean onHoverEvent(MotionEvent event) { - Context context = getContext(); + fun onHoverEvent(event: MotionEvent): Boolean { + val context: Context = getContext() // only send accessibility events if accessibility and exploration are // on. - AccessibilityManager am = (AccessibilityManager) context - .getSystemService(Service.ACCESSIBILITY_SERVICE); + val am: AccessibilityManager = context + .getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager if (!am.isEnabled() || !am.isTouchExplorationEnabled()) { - return super.onHoverEvent(event); + return super.onHoverEvent(event) } - if (event.getAction() != MotionEvent.ACTION_HOVER_EXIT) { - Time hover = getDayFromLocation(event.getX()); + if (event.getAction() !== MotionEvent.ACTION_HOVER_EXIT) { + val hover: Time? = getDayFromLocation(event.getX()) if (hover != null - && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) != 0)) { - Long millis = hover.toMillis(true); - String date = Utils.formatDateRange(context, millis, millis, - DateUtils.FORMAT_SHOW_DATE); - AccessibilityEvent accessEvent = AccessibilityEvent - .obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); - accessEvent.getText().add(date); + && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) !== 0)) { + val millis: Long = hover.toMillis(true) + val date: String = Utils.formatDateRange(context, millis, millis, + DateUtils.FORMAT_SHOW_DATE) + val accessEvent: AccessibilityEvent = AccessibilityEvent + .obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) + accessEvent.getText().add(date) if (mShowDetailsInMonth && mEvents != null) { - int dayStart = SPACING_WEEK_NUMBER + mPadding; - int dayPosition = (int) ((event.getX() - dayStart) * mNumDays / (mWidth - - dayStart - mPadding)); - ArrayList events = mEvents.get(dayPosition); - List text = accessEvent.getText(); - for (Event e : events) { - text.add(e.getTitleAndLocation() + ". "); - int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; + val dayStart: Int = SPACING_WEEK_NUMBER + mPadding + val dayPosition = ((event.getX() - dayStart) * mNumDays / (mWidth + - dayStart - mPadding)) as Int + val events: ArrayList = mEvents!![dayPosition] + val text: List = accessEvent.getText() + for (e in events) { + text.add(e.getTitleAndLocation().toString() + ". ") + var flags: Int = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_YEAR if (!e.allDay) { - flags |= DateUtils.FORMAT_SHOW_TIME; + flags = flags or DateUtils.FORMAT_SHOW_TIME if (DateFormat.is24HourFormat(context)) { - flags |= DateUtils.FORMAT_24HOUR; + flags = flags or DateUtils.FORMAT_24HOUR } } else { - flags |= DateUtils.FORMAT_UTC; + flags = flags or DateUtils.FORMAT_UTC } text.add(Utils.formatDateRange(context, e.startMillis, e.endMillis, - flags) + ". "); + flags).toString() + ". ") } } - sendAccessibilityEventUnchecked(accessEvent); - mLastHoverTime = hover; + sendAccessibilityEventUnchecked(accessEvent) + mLastHoverTime = hover } } - return true; + return true } - public void setClickedDay(float xLocation) { - mClickedDayIndex = getDayIndexFromLocation(xLocation); - invalidate(); + fun setClickedDay(xLocation: Float) { + mClickedDayIndex = getDayIndexFromLocation(xLocation) + invalidate() } - public void clearClickedDay() { - mClickedDayIndex = -1; - invalidate(); + + fun clearClickedDay() { + mClickedDayIndex = -1 + invalidate() + } + + companion object { + private const val TAG = "MonthView" + private const val DEBUG_LAYOUT = false + const val VIEW_PARAMS_ORIENTATION = "orientation" + const val VIEW_PARAMS_ANIMATE_TODAY = "animate_today" + + /* NOTE: these are not constants, and may be multiplied by a scale factor */ + private var TEXT_SIZE_MONTH_NUMBER = 32 + private var TEXT_SIZE_EVENT = 12 + private var TEXT_SIZE_EVENT_TITLE = 14 + private var TEXT_SIZE_MORE_EVENTS = 12 + private var TEXT_SIZE_MONTH_NAME = 14 + private var TEXT_SIZE_WEEK_NUM = 12 + private var DNA_MARGIN = 4 + private var DNA_ALL_DAY_HEIGHT = 4 + private var DNA_MIN_SEGMENT_HEIGHT = 4 + private var DNA_WIDTH = 8 + private var DNA_ALL_DAY_WIDTH = 32 + private var DNA_SIDE_PADDING = 6 + private var CONFLICT_COLOR: Int = Color.BLACK + private var EVENT_TEXT_COLOR: Int = Color.WHITE + private var DEFAULT_EDGE_SPACING = 0 + private var SIDE_PADDING_MONTH_NUMBER = 4 + private var TOP_PADDING_MONTH_NUMBER = 4 + private var TOP_PADDING_WEEK_NUMBER = 4 + private var SIDE_PADDING_WEEK_NUMBER = 20 + private var DAY_SEPARATOR_OUTER_WIDTH = 0 + private var DAY_SEPARATOR_INNER_WIDTH = 1 + private var DAY_SEPARATOR_VERTICAL_LENGTH = 53 + private var DAY_SEPARATOR_VERTICAL_LENGTH_PORTRAIT = 64 + private const val MIN_WEEK_WIDTH = 50 + private var EVENT_X_OFFSET_LANDSCAPE = 38 + private var EVENT_Y_OFFSET_LANDSCAPE = 8 + private var EVENT_Y_OFFSET_PORTRAIT = 7 + private var EVENT_SQUARE_WIDTH = 10 + private var EVENT_SQUARE_BORDER = 2 + private var EVENT_LINE_PADDING = 2 + private var EVENT_RIGHT_PADDING = 4 + private var EVENT_BOTTOM_PADDING = 3 + private var TODAY_HIGHLIGHT_WIDTH = 2 + private var SPACING_WEEK_NUMBER = 24 + private var mInitialized = false + private var mShowDetailsInMonth = false + protected var mStringBuilder: StringBuilder = StringBuilder(50) + + // TODO recreate formatter when locale changes + protected var mFormatter: Formatter = Formatter(mStringBuilder, Locale.getDefault()) + private const val mClickedAlpha = 128 } -} +} \ No newline at end of file -- cgit v1.2.3 From 301173aaec7a75b03302bd6e8fe33a3072aa6039 Mon Sep 17 00:00:00 2001 From: Mahi Kolla Date: Mon, 12 Jul 2021 19:02:47 +0000 Subject: AOSP/Calendar - Add Kotlin code for MonthWeekEventsView.kt. Also uploaded corresponding Android.bp file This class contained a lot of calculations which resulted in needing to add .toFloat() or .toInt() to these expressions. In addition, as a subclass of SimpleWeekView, some of the methods and variables needed override modifiers. A few changes were made in SimpleWeekView such as variable declarations to accomodate the way the variables are used in MonthWeekEventsView. Test: manual (ran build and unit tests) and CTS testing suite $ source build/envsetup.sh $ lunch aosp_bonito-userdebug $ make Calendar $ adb install -r -d -t out/target/product/bonito/product/app/Calendar/Calendar.apk $ make CalendarTests -j $ adb install -r -d -t out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.15 OK (22 tests) CTS TESTING: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ============================================ ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 7m 28s Total aggregated tests run time: 7m 28s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.84 tests/sec [376 tests / 448391 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 12537 ms || clean = 2820 ms Total preparation time: 12s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 9m 26s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== ============================================ Change-Id: I9941c06d860aefc3382f5314c817da44ac0db088 --- Android.bp | 1 + .../android/calendar/month/MonthWeekEventsView.kt | 394 ++++++++++++--------- src/com/android/calendar/month/SimpleWeekView.kt | 64 ++-- 3 files changed, 252 insertions(+), 207 deletions(-) diff --git a/Android.bp b/Android.bp index 41d25270..2fb4ad1c 100644 --- a/Android.bp +++ b/Android.bp @@ -46,6 +46,7 @@ exclude_srcsd = [ exclude_srcsm = [ "src/**/calendar/month/MonthListView.java", + "src/**/calendar/month/MonthWeekEventsView.java", "src/**/calendar/month/SimpleWeekView.java", "src/**/calendar/month/SimpleDayPickerFragment.java", "src/**/calendar/AsyncQueryServiceHelper.java", diff --git a/src/com/android/calendar/month/MonthWeekEventsView.kt b/src/com/android/calendar/month/MonthWeekEventsView.kt index adaf70dc..e4b15494 100644 --- a/src/com/android/calendar/month/MonthWeekEventsView.kt +++ b/src/com/android/calendar/month/MonthWeekEventsView.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -16,34 +16,68 @@ package com.android.calendar.month import com.android.calendar.Event +import com.android.calendar.R +import com.android.calendar.Utils +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ObjectAnimator +import android.app.Service +import android.content.Context +import android.content.res.Configuration +import android.content.res.Resources +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Paint.Align +import android.graphics.Paint.Style +import android.graphics.Typeface +import android.graphics.drawable.Drawable +import android.provider.CalendarContract.Attendees +import android.text.TextPaint +import android.text.TextUtils +import android.text.format.DateFormat +import android.text.format.DateUtils +import android.text.format.Time +import android.util.Log +import android.view.MotionEvent +import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityManager +import java.util.ArrayList +import java.util.Arrays +import java.util.Formatter +import java.util.HashMap +import java.util.Iterator +import java.util.List +import java.util.Locale class MonthWeekEventsView /** * Shows up as an error if we don't include this. */ -(context: Context?) : SimpleWeekView(context) { - protected var mToday: Time = Time() - protected var mHasToday = false +(context: Context) : SimpleWeekView(context) { + // Renamed to avoid override modifier and type mismatch error + protected val mTodayTime: Time = Time() + override protected var mHasToday = false protected var mTodayIndex = -1 protected var mOrientation: Int = Configuration.ORIENTATION_LANDSCAPE protected var mEvents: List>? = null protected var mUnsortedEvents: ArrayList? = null - var mDna: HashMap? = null + var mDna: HashMap? = null // This is for drawing the outlines around event chips and supports up to 10 // events being drawn on each day. The code will expand this if necessary. - protected var mEventOutlines = FloatRef(10 * 4 * 4 * 7) + protected var mEventOutlines: FloatRef = FloatRef(10 * 4 * 4 * 7) protected var mMonthNamePaint: Paint? = null - protected var mEventPaint: TextPaint? = null + protected var mEventPaint: TextPaint = TextPaint() protected var mSolidBackgroundEventPaint: TextPaint? = null protected var mFramedEventPaint: TextPaint? = null protected var mDeclinedEventPaint: TextPaint? = null - protected var mEventExtrasPaint: TextPaint? = null - protected var mEventDeclinedExtrasPaint: TextPaint? = null - protected var mWeekNumPaint: Paint? = null - protected var mDNAAllDayPaint: Paint? = null - protected var mDNATimePaint: Paint? = null - protected var mEventSquarePaint: Paint? = null + protected var mEventExtrasPaint: TextPaint = TextPaint() + protected var mEventDeclinedExtrasPaint: TextPaint = TextPaint() + protected var mWeekNumPaint: Paint = Paint() + protected var mDNAAllDayPaint: Paint = Paint() + protected var mDNATimePaint: Paint = Paint() + protected var mEventSquarePaint: Paint = Paint() protected var mTodayDrawable: Drawable? = null protected var mMonthNumHeight = 0 protected var mMonthNumAscentHeight = 0 @@ -88,7 +122,7 @@ class MonthWeekEventsView @Volatile private var mFadingIn = false @Override - fun onAnimationEnd(animation: Animator) { + override fun onAnimationEnd(animation: Animator) { synchronized(this) { if (mAnimator !== animation) { animation.removeAllListeners() @@ -97,20 +131,20 @@ class MonthWeekEventsView } if (mFadingIn) { if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners() - mTodayAnimator.cancel() + mTodayAnimator?.removeAllListeners() + mTodayAnimator?.cancel() } mTodayAnimator = ObjectAnimator.ofInt(this@MonthWeekEventsView, "animateTodayAlpha", 255, 0) mAnimator = mTodayAnimator mFadingIn = false - mTodayAnimator.addListener(this) - mTodayAnimator.setDuration(600) - mTodayAnimator.start() + mTodayAnimator?.addListener(this) + mTodayAnimator?.setDuration(600) + mTodayAnimator?.start() } else { mAnimateToday = false mAnimateTodayAlpha = 0 - mAnimator.removeAllListeners() + mAnimator?.removeAllListeners() mAnimator = null mTodayAnimator = null invalidate() @@ -127,13 +161,13 @@ class MonthWeekEventsView } } - private var mDayXs: IntArray? + private var mDayXs: IntArray? = null /** * This provides a reference to a float array which allows for easy size * checking and reallocation. Used for drawing lines. */ - private inner class FloatRef(size: Int) { + inner class FloatRef(size: Int) { var array: FloatArray fun ensureSize(newSize: Int) { if (newSize >= array.size) { @@ -175,13 +209,13 @@ class MonthWeekEventsView } // Create the drawing coordinates for dna if (!mShowDetailsInMonth) { - val numDays: Int = mEvents!!.size() + val numDays: Int = mEvents!!.size var effectiveWidth: Int = mWidth - mPadding * 2 if (mShowWeekNum) { effectiveWidth -= SPACING_WEEK_NUMBER } DNA_ALL_DAY_WIDTH = effectiveWidth / numDays - 2 * DNA_SIDE_PADDING - mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH) + mDNAAllDayPaint?.setStrokeWidth(DNA_ALL_DAY_WIDTH.toFloat()) mDayXs = IntArray(numDays) for (day in 0 until numDays) { mDayXs!![day] = computeDayLeftPosition(day) + DNA_WIDTH / 2 + DNA_SIDE_PADDING @@ -198,10 +232,10 @@ class MonthWeekEventsView if (sortedEvents == null) { return } - if (sortedEvents.size() !== mNumDays) { + if (sortedEvents.size !== mNumDays) { if (Log.isLoggable(TAG, Log.ERROR)) { Log.wtf(TAG, ("Events size must be same as days displayed: size=" - + sortedEvents.size()) + " days=" + mNumDays) + + sortedEvents.size) + " days=" + mNumDays) } mEvents = null return @@ -236,7 +270,7 @@ class MonthWeekEventsView * want to use a different paint. */ @Override - protected fun initView() { + protected override fun initView() { super.initView() if (!mInitialized) { val resources: Resources = getContext().getResources() @@ -246,38 +280,38 @@ class MonthWeekEventsView SIDE_PADDING_MONTH_NUMBER = resources.getInteger(R.integer.month_day_number_margin) CONFLICT_COLOR = resources.getColor(R.color.month_dna_conflict_time_color) EVENT_TEXT_COLOR = resources.getColor(R.color.calendar_event_text_color) - if (mScale !== 1) { - TOP_PADDING_MONTH_NUMBER *= mScale - TOP_PADDING_WEEK_NUMBER *= mScale - SIDE_PADDING_MONTH_NUMBER *= mScale - SIDE_PADDING_WEEK_NUMBER *= mScale - SPACING_WEEK_NUMBER *= mScale - TEXT_SIZE_MONTH_NUMBER *= mScale - TEXT_SIZE_EVENT *= mScale - TEXT_SIZE_EVENT_TITLE *= mScale - TEXT_SIZE_MORE_EVENTS *= mScale - TEXT_SIZE_MONTH_NAME *= mScale - TEXT_SIZE_WEEK_NUM *= mScale - DAY_SEPARATOR_OUTER_WIDTH *= mScale - DAY_SEPARATOR_INNER_WIDTH *= mScale - DAY_SEPARATOR_VERTICAL_LENGTH *= mScale - DAY_SEPARATOR_VERTICAL_LENGTH_PORTRAIT *= mScale - EVENT_X_OFFSET_LANDSCAPE *= mScale - EVENT_Y_OFFSET_LANDSCAPE *= mScale - EVENT_Y_OFFSET_PORTRAIT *= mScale - EVENT_SQUARE_WIDTH *= mScale - EVENT_SQUARE_BORDER *= mScale - EVENT_LINE_PADDING *= mScale - EVENT_BOTTOM_PADDING *= mScale - EVENT_RIGHT_PADDING *= mScale - DNA_MARGIN *= mScale - DNA_WIDTH *= mScale - DNA_ALL_DAY_HEIGHT *= mScale - DNA_MIN_SEGMENT_HEIGHT *= mScale - DNA_SIDE_PADDING *= mScale - DEFAULT_EDGE_SPACING *= mScale - DNA_ALL_DAY_WIDTH *= mScale - TODAY_HIGHLIGHT_WIDTH *= mScale + if (mScale != 1f) { + TOP_PADDING_MONTH_NUMBER *= mScale.toInt() + TOP_PADDING_WEEK_NUMBER *= mScale.toInt() + SIDE_PADDING_MONTH_NUMBER *= mScale.toInt() + SIDE_PADDING_WEEK_NUMBER *= mScale.toInt() + SPACING_WEEK_NUMBER *= mScale.toInt() + TEXT_SIZE_MONTH_NUMBER *= mScale.toInt() + TEXT_SIZE_EVENT *= mScale.toInt() + TEXT_SIZE_EVENT_TITLE *= mScale.toInt() + TEXT_SIZE_MORE_EVENTS *= mScale.toInt() + TEXT_SIZE_MONTH_NAME *= mScale.toInt() + TEXT_SIZE_WEEK_NUM *= mScale.toInt() + DAY_SEPARATOR_OUTER_WIDTH *= mScale.toInt() + DAY_SEPARATOR_INNER_WIDTH *= mScale.toInt() + DAY_SEPARATOR_VERTICAL_LENGTH *= mScale.toInt() + DAY_SEPARATOR_VERTICAL_LENGTH_PORTRAIT *= mScale.toInt() + EVENT_X_OFFSET_LANDSCAPE *= mScale.toInt() + EVENT_Y_OFFSET_LANDSCAPE *= mScale.toInt() + EVENT_Y_OFFSET_PORTRAIT *= mScale.toInt() + EVENT_SQUARE_WIDTH *= mScale.toInt() + EVENT_SQUARE_BORDER *= mScale.toInt() + EVENT_LINE_PADDING *= mScale.toInt() + EVENT_BOTTOM_PADDING *= mScale.toInt() + EVENT_RIGHT_PADDING *= mScale.toInt() + DNA_MARGIN *= mScale.toInt() + DNA_WIDTH *= mScale.toInt() + DNA_ALL_DAY_HEIGHT *= mScale.toInt() + DNA_MIN_SEGMENT_HEIGHT *= mScale.toInt() + DNA_SIDE_PADDING *= mScale.toInt() + DEFAULT_EDGE_SPACING *= mScale.toInt() + DNA_ALL_DAY_WIDTH *= mScale.toInt() + TODAY_HIGHLIGHT_WIDTH *= mScale.toInt() } if (!mShowDetailsInMonth) { TOP_PADDING_MONTH_NUMBER += DNA_ALL_DAY_HEIGHT + DNA_MARGIN @@ -288,106 +322,110 @@ class MonthWeekEventsView loadColors(getContext()) // TODO modify paint properties depending on isMini mMonthNumPaint = Paint() - mMonthNumPaint.setFakeBoldText(false) - mMonthNumPaint.setAntiAlias(true) - mMonthNumPaint.setTextSize(TEXT_SIZE_MONTH_NUMBER) - mMonthNumPaint.setColor(mMonthNumColor) - mMonthNumPaint.setStyle(Style.FILL) - mMonthNumPaint.setTextAlign(Align.RIGHT) - mMonthNumPaint.setTypeface(Typeface.DEFAULT) - mMonthNumAscentHeight = (-mMonthNumPaint.ascent() + 0.5f) - mMonthNumHeight = (mMonthNumPaint.descent() - mMonthNumPaint.ascent() + 0.5f) + mMonthNumPaint?.setFakeBoldText(false) + mMonthNumPaint?.setAntiAlias(true) + mMonthNumPaint?.setTextSize(TEXT_SIZE_MONTH_NUMBER.toFloat()) + mMonthNumPaint?.setColor(mMonthNumColor) + mMonthNumPaint?.setStyle(Style.FILL) + mMonthNumPaint?.setTextAlign(Align.RIGHT) + mMonthNumPaint?.setTypeface(Typeface.DEFAULT) + mMonthNumAscentHeight = (-mMonthNumPaint!!.ascent() + 0.5f).toInt() + mMonthNumHeight = (mMonthNumPaint!!.descent() - mMonthNumPaint!!.ascent() + 0.5f).toInt() mEventPaint = TextPaint() - mEventPaint.setFakeBoldText(true) - mEventPaint.setAntiAlias(true) - mEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE) - mEventPaint.setColor(mMonthEventColor) + mEventPaint?.setFakeBoldText(true) + mEventPaint?.setAntiAlias(true) + mEventPaint?.setTextSize(TEXT_SIZE_EVENT_TITLE.toFloat()) + mEventPaint?.setColor(mMonthEventColor) mSolidBackgroundEventPaint = TextPaint(mEventPaint) - mSolidBackgroundEventPaint.setColor(EVENT_TEXT_COLOR) + mSolidBackgroundEventPaint?.setColor(EVENT_TEXT_COLOR) mFramedEventPaint = TextPaint(mSolidBackgroundEventPaint) mDeclinedEventPaint = TextPaint() - mDeclinedEventPaint.setFakeBoldText(true) - mDeclinedEventPaint.setAntiAlias(true) - mDeclinedEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE) - mDeclinedEventPaint.setColor(mMonthDeclinedEventColor) - mEventAscentHeight = (-mEventPaint.ascent() + 0.5f) - mEventHeight = (mEventPaint.descent() - mEventPaint.ascent() + 0.5f) + mDeclinedEventPaint?.setFakeBoldText(true) + mDeclinedEventPaint?.setAntiAlias(true) + mDeclinedEventPaint?.setTextSize(TEXT_SIZE_EVENT_TITLE.toFloat()) + mDeclinedEventPaint?.setColor(mMonthDeclinedEventColor) + mEventAscentHeight = (-mEventPaint.ascent() + 0.5f).toInt() + mEventHeight = (mEventPaint.descent() - mEventPaint.ascent() + 0.5f).toInt() mEventExtrasPaint = TextPaint() - mEventExtrasPaint.setFakeBoldText(false) - mEventExtrasPaint.setAntiAlias(true) - mEventExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER) - mEventExtrasPaint.setTextSize(TEXT_SIZE_EVENT) - mEventExtrasPaint.setColor(mMonthEventExtraColor) - mEventExtrasPaint.setStyle(Style.FILL) - mEventExtrasPaint.setTextAlign(Align.LEFT) - mExtrasHeight = (mEventExtrasPaint.descent() - mEventExtrasPaint.ascent() + 0.5f) - mExtrasAscentHeight = (-mEventExtrasPaint.ascent() + 0.5f) - mExtrasDescent = (mEventExtrasPaint.descent() + 0.5f) + mEventExtrasPaint?.setFakeBoldText(false) + mEventExtrasPaint?.setAntiAlias(true) + mEventExtrasPaint?.setStrokeWidth(EVENT_SQUARE_BORDER.toFloat()) + mEventExtrasPaint?.setTextSize(TEXT_SIZE_EVENT.toFloat()) + mEventExtrasPaint?.setColor(mMonthEventExtraColor) + mEventExtrasPaint?.setStyle(Style.FILL) + mEventExtrasPaint?.setTextAlign(Align.LEFT) + mExtrasHeight = (mEventExtrasPaint.descent() - mEventExtrasPaint.ascent() + 0.5f).toInt() + mExtrasAscentHeight = (-mEventExtrasPaint.ascent() + 0.5f).toInt() + mExtrasDescent = (mEventExtrasPaint.descent() + 0.5f).toInt() mEventDeclinedExtrasPaint = TextPaint() mEventDeclinedExtrasPaint.setFakeBoldText(false) mEventDeclinedExtrasPaint.setAntiAlias(true) - mEventDeclinedExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER) - mEventDeclinedExtrasPaint.setTextSize(TEXT_SIZE_EVENT) + mEventDeclinedExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER.toFloat()) + mEventDeclinedExtrasPaint.setTextSize(TEXT_SIZE_EVENT.toFloat()) mEventDeclinedExtrasPaint.setColor(mMonthDeclinedExtrasColor) mEventDeclinedExtrasPaint.setStyle(Style.FILL) mEventDeclinedExtrasPaint.setTextAlign(Align.LEFT) mWeekNumPaint = Paint() mWeekNumPaint.setFakeBoldText(false) mWeekNumPaint.setAntiAlias(true) - mWeekNumPaint.setTextSize(TEXT_SIZE_WEEK_NUM) + mWeekNumPaint.setTextSize(TEXT_SIZE_WEEK_NUM.toFloat()) mWeekNumPaint.setColor(mWeekNumColor) mWeekNumPaint.setStyle(Style.FILL) mWeekNumPaint.setTextAlign(Align.RIGHT) - mWeekNumAscentHeight = (-mWeekNumPaint.ascent() + 0.5f) + mWeekNumAscentHeight = (-mWeekNumPaint.ascent() + 0.5f).toInt() mDNAAllDayPaint = Paint() mDNATimePaint = Paint() mDNATimePaint.setColor(mMonthBusyBitsBusyTimeColor) mDNATimePaint.setStyle(Style.FILL_AND_STROKE) - mDNATimePaint.setStrokeWidth(DNA_WIDTH) + mDNATimePaint.setStrokeWidth(DNA_WIDTH.toFloat()) mDNATimePaint.setAntiAlias(false) mDNAAllDayPaint.setColor(mMonthBusyBitsConflictTimeColor) mDNAAllDayPaint.setStyle(Style.FILL_AND_STROKE) - mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH) + mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH.toFloat()) mDNAAllDayPaint.setAntiAlias(false) mEventSquarePaint = Paint() - mEventSquarePaint.setStrokeWidth(EVENT_SQUARE_BORDER) + mEventSquarePaint.setStrokeWidth(EVENT_SQUARE_BORDER.toFloat()) mEventSquarePaint.setAntiAlias(false) if (DEBUG_LAYOUT) { Log.d("EXTRA", "mScale=$mScale") - Log.d("EXTRA", "mMonthNumPaint ascent=" + mMonthNumPaint.ascent() - .toString() + " descent=" + mMonthNumPaint.descent().toString() + " int height=" + mMonthNumHeight) - Log.d("EXTRA", "mEventPaint ascent=" + mEventPaint.ascent() - .toString() + " descent=" + mEventPaint.descent().toString() + " int height=" + mEventHeight + Log.d("EXTRA", "mMonthNumPaint ascent=" + mMonthNumPaint?.ascent() + ?.toString() + " descent=" + mMonthNumPaint?.descent()?.toString() + + " int height=" + mMonthNumHeight) + Log.d("EXTRA", "mEventPaint ascent=" + mEventPaint?.ascent() + ?.toString() + " descent=" + mEventPaint.descent().toString() + + " int height=" + mEventHeight .toString() + " int ascent=" + mEventAscentHeight) Log.d("EXTRA", "mEventExtrasPaint ascent=" + mEventExtrasPaint.ascent() - .toString() + " descent=" + mEventExtrasPaint.descent().toString() + " int height=" + mExtrasHeight) + .toString() + " descent=" + mEventExtrasPaint.descent().toString() + + " int height=" + mExtrasHeight) Log.d("EXTRA", "mWeekNumPaint ascent=" + mWeekNumPaint.ascent() .toString() + " descent=" + mWeekNumPaint.descent()) } } @Override - fun setWeekParams(params: HashMap, tz: String) { + override fun setWeekParams(params: HashMap, tz: String) { super.setWeekParams(params, tz) if (params.containsKey(VIEW_PARAMS_ORIENTATION)) { - mOrientation = params.get(VIEW_PARAMS_ORIENTATION) + mOrientation = params.get(VIEW_PARAMS_ORIENTATION) ?: + Configuration.ORIENTATION_LANDSCAPE } updateToday(tz) mNumCells = mNumDays + 1 if (params.containsKey(VIEW_PARAMS_ANIMATE_TODAY) && mHasToday) { synchronized(mAnimatorListener) { if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners() - mTodayAnimator.cancel() + mTodayAnimator?.removeAllListeners() + mTodayAnimator?.cancel() } mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha", Math.max(mAnimateTodayAlpha, 80), 255) - mTodayAnimator.setDuration(150) + mTodayAnimator?.setDuration(150) mAnimatorListener.setAnimator(mTodayAnimator) mAnimatorListener.setFadingIn(true) - mTodayAnimator.addListener(mAnimatorListener) + mTodayAnimator?.addListener(mAnimatorListener) mAnimateToday = true - mTodayAnimator.start() + mTodayAnimator?.start() } } } @@ -396,10 +434,10 @@ class MonthWeekEventsView * @param tz */ fun updateToday(tz: String): Boolean { - mToday.timezone = tz - mToday.setToNow() - mToday.normalize(true) - val julianToday: Int = Time.getJulianDay(mToday.toMillis(false), mToday.gmtoff) + mTodayTime.timezone = tz + mTodayTime.setToNow() + mTodayTime.normalize(true) + val julianToday: Int = Time.getJulianDay(mTodayTime.toMillis(false), mTodayTime.gmtoff) if (julianToday >= mFirstJulianDay && julianToday < mFirstJulianDay + mNumDays) { mHasToday = true mTodayIndex = julianToday - mFirstJulianDay @@ -416,7 +454,7 @@ class MonthWeekEventsView } @Override - protected fun onDraw(canvas: Canvas) { + protected override fun onDraw(canvas: Canvas) { drawBackground(canvas) drawWeekNums(canvas) drawDaySeparators(canvas) @@ -436,12 +474,12 @@ class MonthWeekEventsView protected fun drawToday(canvas: Canvas) { r.top = DAY_SEPARATOR_INNER_WIDTH + TODAY_HIGHLIGHT_WIDTH / 2 - r.bottom = mHeight - Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f) as Int + r.bottom = mHeight - Math.ceil(TODAY_HIGHLIGHT_WIDTH.toDouble() / 2.0f).toInt() p.setStyle(Style.STROKE) - p.setStrokeWidth(TODAY_HIGHLIGHT_WIDTH) + p.setStrokeWidth(TODAY_HIGHLIGHT_WIDTH.toFloat()) r.left = computeDayLeftPosition(mTodayIndex) + TODAY_HIGHLIGHT_WIDTH / 2 r.right = (computeDayLeftPosition(mTodayIndex + 1) - - Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f) as Int) + - Math.ceil(TODAY_HIGHLIGHT_WIDTH.toDouble() / 2.0f).toInt()) p.setColor(mTodayAnimateColor or (mAnimateTodayAlpha shl 24)) canvas.drawRect(r, p) p.setStyle(Style.FILL) @@ -462,7 +500,7 @@ class MonthWeekEventsView } @Override - protected fun drawDaySeparators(canvas: Canvas) { + protected override fun drawDaySeparators(canvas: Canvas) { val lines = FloatArray(8 * 4) var count = 6 * 4 var wkNumOffset = 0 @@ -472,16 +510,16 @@ class MonthWeekEventsView val xOffset: Int = SPACING_WEEK_NUMBER + mPadding count += 4 lines[i++] = xOffset.toFloat() - lines[i++] = 0 + lines[i++] = 0f lines[i++] = xOffset.toFloat() - lines[i++] = mHeight + lines[i++] = mHeight.toFloat() wkNumOffset++ } count += 4 - lines[i++] = 0 - lines[i++] = 0 - lines[i++] = mWidth - lines[i++] = 0 + lines[i++] = 0f + lines[i++] = 0f + lines[i++] = mWidth.toFloat() + lines[i++] = 0f val y0 = 0 val y1: Int = mHeight while (i < count) { @@ -492,12 +530,12 @@ class MonthWeekEventsView lines[i++] = y1.toFloat() } p.setColor(mDaySeparatorInnerColor) - p.setStrokeWidth(DAY_SEPARATOR_INNER_WIDTH) + p.setStrokeWidth(DAY_SEPARATOR_INNER_WIDTH.toFloat()) canvas.drawLines(lines, 0, count, p) } @Override - protected fun drawBackground(canvas: Canvas) { + protected override fun drawBackground(canvas: Canvas) { var i = 0 var offset = 0 r.top = DAY_SEPARATOR_INNER_WIDTH @@ -506,15 +544,15 @@ class MonthWeekEventsView i++ offset++ } - if (!mOddMonth.get(i)) { - while (++i < mOddMonth.length && !mOddMonth.get(i)); + if (!mOddMonth!!.get(i)) { + while (++i < mOddMonth!!.size && !mOddMonth!!.get(i)); r.right = computeDayLeftPosition(i - offset) r.left = 0 p.setColor(mMonthBGOtherColor) canvas.drawRect(r, p) // compute left edge for i, set up r, draw - } else if (!mOddMonth.get(mOddMonth.length - 1.also { i = it })) { - while (--i >= offset && !mOddMonth.get(i)); + } else if (!mOddMonth!!.get(mOddMonth!!.size - 1.also { i = it })) { + while (--i >= offset && !mOddMonth!!.get(i)); i++ // compute left edge for i, set up r, draw r.right = mWidth @@ -546,7 +584,7 @@ class MonthWeekEventsView } @Override - protected fun drawWeekNums(canvas: Canvas) { + protected override fun drawWeekNums(canvas: Canvas) { var y: Int var i = 0 var offset = -1 @@ -556,33 +594,34 @@ class MonthWeekEventsView if (mShowWeekNum) { x = SIDE_PADDING_WEEK_NUMBER + mPadding y = mWeekNumAscentHeight + TOP_PADDING_WEEK_NUMBER - canvas.drawText(mDayNumbers.get(0), x, y, mWeekNumPaint) + canvas.drawText(mDayNumbers!!.get(0) as String, x.toFloat(), y.toFloat(), mWeekNumPaint) numCount++ i++ todayIndex++ offset++ } y = mMonthNumAscentHeight + TOP_PADDING_MONTH_NUMBER - var isFocusMonth: Boolean = mFocusDay.get(i) + var isFocusMonth: Boolean = mFocusDay!!.get(i) var isBold = false - mMonthNumPaint.setColor(if (isFocusMonth) mMonthNumColor else mMonthNumOtherColor) + mMonthNumPaint?.setColor(if (isFocusMonth) mMonthNumColor else mMonthNumOtherColor) while (i < numCount) { if (mHasToday && todayIndex == i) { - mMonthNumPaint.setColor(mMonthNumTodayColor) - mMonthNumPaint.setFakeBoldText(true.also { isBold = it }) + mMonthNumPaint?.setColor(mMonthNumTodayColor) + mMonthNumPaint?.setFakeBoldText(true.also { isBold = it }) if (i + 1 < numCount) { // Make sure the color will be set back on the next // iteration - isFocusMonth = !mFocusDay.get(i + 1) + isFocusMonth = !mFocusDay!!.get(i + 1) } - } else if (mFocusDay.get(i) !== isFocusMonth) { - isFocusMonth = mFocusDay.get(i) - mMonthNumPaint.setColor(if (isFocusMonth) mMonthNumColor else mMonthNumOtherColor) + } else if (mFocusDay?.get(i) !== isFocusMonth) { + isFocusMonth = mFocusDay!!.get(i) + mMonthNumPaint?.setColor(if (isFocusMonth) mMonthNumColor else mMonthNumOtherColor) } x = computeDayLeftPosition(i - offset) - SIDE_PADDING_MONTH_NUMBER - canvas.drawText(mDayNumbers.get(i), x, y, mMonthNumPaint) + canvas.drawText(mDayNumbers!!.get(i) as String, x.toFloat(), y.toFloat(), + mMonthNumPaint as Paint) if (isBold) { - mMonthNumPaint.setFakeBoldText(false.also { isBold = it }) + mMonthNumPaint?.setFakeBoldText(false.also { isBold = it }) } i++ } @@ -595,7 +634,7 @@ class MonthWeekEventsView var day = -1 for (eventDay in mEvents!!) { day++ - if (eventDay == null || eventDay.size() === 0) { + if (eventDay == null || eventDay.size === 0) { continue } var ySquare: Int @@ -611,7 +650,7 @@ class MonthWeekEventsView // Determine if everything will fit when time ranges are shown. var showTimes = true - var iter: Iterator = eventDay.iterator() + var iter: Iterator = eventDay.iterator() as Iterator var yTest = ySquare while (iter.hasNext()) { val event: Event = iter.next() @@ -624,7 +663,7 @@ class MonthWeekEventsView yTest = newY } var eventCount = 0 - iter = eventDay.iterator() + iter = eventDay.iterator() as Iterator while (iter.hasNext()) { val event: Event = iter.next() val newY = drawEvent(canvas, event, xSquare, ySquare, rightEdge, iter.hasNext(), @@ -635,7 +674,7 @@ class MonthWeekEventsView eventCount++ ySquare = newY } - val remaining: Int = eventDay.size() - eventCount + val remaining: Int = eventDay.size- eventCount if (remaining > 0) { drawMoreEvents(canvas, remaining, xSquare) } @@ -774,7 +813,7 @@ class MonthWeekEventsView val avail = (textRightEdge - textX).toFloat() var text: CharSequence = TextUtils.ellipsize( event.title, mEventPaint, avail, TextUtils.TruncateAt.END) - val textPaint: Paint + val textPaint: TextPaint? textPaint = if (solidBackground) { // Text color needs to contrast with solid background. mSolidBackgroundEventPaint @@ -783,13 +822,13 @@ class MonthWeekEventsView mDeclinedEventPaint } else if (allDay) { // Text inside frame is same color as frame. - mFramedEventPaint.setColor(color) + mFramedEventPaint?.setColor(color) mFramedEventPaint } else { // Use generic event text color. mEventPaint } - canvas.drawText(text.toString(), textX, textY, textPaint) + canvas.drawText(text.toString(), textX.toFloat(), textY.toFloat(), textPaint as Paint) y += mEventHeight if (allDay) { y += BORDER_SPACE * 2 @@ -802,7 +841,8 @@ class MonthWeekEventsView event.endMillis, DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_ABBREV_ALL, Utils.getTimeZone(getContext(), null)).toString() text = TextUtils.ellipsize(text, mEventExtrasPaint, avail, TextUtils.TruncateAt.END) - canvas.drawText(text.toString(), textX, textY, if (isDeclined) mEventDeclinedExtrasPaint else mEventExtrasPaint) + canvas.drawText(text.toString(), textX.toFloat(), textY.toFloat(), + if (isDeclined) mEventDeclinedExtrasPaint else mEventExtrasPaint) y += mExtrasHeight } y += EVENT_LINE_PADDING @@ -815,8 +855,9 @@ class MonthWeekEventsView R.plurals.month_more_events, remainingEvents) mEventExtrasPaint.setAntiAlias(true) mEventExtrasPaint.setFakeBoldText(true) - canvas.drawText(String.format(text, remainingEvents), x, y, mEventExtrasPaint) - mEventExtrasPaint.setFakeBoldText(false) + canvas.drawText(String.format(text, remainingEvents), x.toFloat(), y.toFloat(), + mEventExtrasPaint as Paint) + mEventExtrasPaint!!.setFakeBoldText(false) } /** @@ -829,32 +870,35 @@ class MonthWeekEventsView protected fun drawDNA(canvas: Canvas) { // Draw event and conflict times if (mDna != null) { - for (strand in mDna.values()) { - if (strand.color === CONFLICT_COLOR || strand.points == null || strand.points.length === 0) { + for (strand in mDna!!.values) { + if (strand.color === CONFLICT_COLOR || strand.points == null || + (strand.points as FloatArray).size === 0) { continue } - mDNATimePaint.setColor(strand.color) - canvas.drawLines(strand.points, mDNATimePaint) + mDNATimePaint!!.setColor(strand.color) + canvas.drawLines(strand.points as FloatArray, mDNATimePaint as Paint) } // Draw black last to make sure it's on top - val strand: Utils.DNAStrand = mDna.get(CONFLICT_COLOR) - if (strand != null && strand.points != null && strand.points.length !== 0) { - mDNATimePaint.setColor(strand.color) - canvas.drawLines(strand.points, mDNATimePaint) + val strand: Utils.DNAStrand? = mDna?.get(CONFLICT_COLOR) + if (strand != null && strand!!.points != null && strand!!.points?.size !== 0) { + mDNATimePaint!!.setColor(strand.color) + canvas.drawLines(strand.points as FloatArray, mDNATimePaint as Paint) } if (mDayXs == null) { return } val numDays = mDayXs!!.size val xOffset = (DNA_ALL_DAY_WIDTH - DNA_WIDTH) / 2 - if (strand != null && strand.allDays != null && strand.allDays.length === numDays) { + if (strand != null && strand!!.allDays != null && strand!!.allDays?.size === numDays) { for (i in 0 until numDays) { // this adds at most 7 draws. We could sort it by color and // build an array instead but this is easier. - if (strand.allDays.get(i) !== 0) { - mDNAAllDayPaint.setColor(strand.allDays.get(i)) - canvas.drawLine(mDayXs!![i] + xOffset, DNA_MARGIN, mDayXs!![i] + xOffset, - DNA_MARGIN + DNA_ALL_DAY_HEIGHT, mDNAAllDayPaint) + if (strand!!.allDays?.get(i) !== 0) { + mDNAAllDayPaint!!.setColor(strand!!.allDays!!.get(i)) + canvas.drawLine(mDayXs!![i].toFloat() + xOffset.toFloat(), + DNA_MARGIN.toFloat(), mDayXs!![i].toFloat() + xOffset.toFloat(), + DNA_MARGIN.toFloat() + DNA_ALL_DAY_HEIGHT.toFloat(), + mDNAAllDayPaint as Paint) } } } @@ -862,7 +906,7 @@ class MonthWeekEventsView } @Override - protected fun updateSelectionPositions() { + protected override fun updateSelectionPositions() { if (mHasSelectedDay) { var selectedPosition: Int = mSelectedDay - mWeekStart if (selectedPosition < 0) { @@ -881,12 +925,12 @@ class MonthWeekEventsView val dayStart: Int = if (mShowWeekNum) SPACING_WEEK_NUMBER + mPadding else mPadding return if (x < dayStart || x > mWidth - mPadding) { -1 - } else ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)) + } else (((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)).toInt()) // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels } @Override - fun getDayFromLocation(x: Float): Time? { + override fun getDayFromLocation(x: Float): Time? { val dayPosition = getDayIndexFromLocation(x) if (dayPosition == -1) { return null @@ -908,7 +952,7 @@ class MonthWeekEventsView } @Override - fun onHoverEvent(event: MotionEvent): Boolean { + override fun onHoverEvent(event: MotionEvent): Boolean { val context: Context = getContext() // only send accessibility events if accessibility and exploration are // on. @@ -922,21 +966,21 @@ class MonthWeekEventsView if (hover != null && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) !== 0)) { val millis: Long = hover.toMillis(true) - val date: String = Utils.formatDateRange(context, millis, millis, - DateUtils.FORMAT_SHOW_DATE) + val date: String = Utils!!.formatDateRange(context, millis, millis, + DateUtils.FORMAT_SHOW_DATE) as String val accessEvent: AccessibilityEvent = AccessibilityEvent .obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) accessEvent.getText().add(date) if (mShowDetailsInMonth && mEvents != null) { val dayStart: Int = SPACING_WEEK_NUMBER + mPadding val dayPosition = ((event.getX() - dayStart) * mNumDays / (mWidth - - dayStart - mPadding)) as Int + - dayStart - mPadding)).toInt() val events: ArrayList = mEvents!![dayPosition] - val text: List = accessEvent.getText() + val text: List = accessEvent.getText() as List for (e in events) { - text.add(e.getTitleAndLocation().toString() + ". ") + text.add(e!!.titleAndLocation.toString() + ". ") var flags: Int = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_YEAR - if (!e.allDay) { + if (!e!!.allDay) { flags = flags or DateUtils.FORMAT_SHOW_TIME if (DateFormat.is24HourFormat(context)) { flags = flags or DateUtils.FORMAT_24HOUR @@ -944,7 +988,7 @@ class MonthWeekEventsView } else { flags = flags or DateUtils.FORMAT_UTC } - text.add(Utils.formatDateRange(context, e.startMillis, e.endMillis, + text.add(Utils.formatDateRange(context, e!!.startMillis, e!!.endMillis, flags).toString() + ". ") } } diff --git a/src/com/android/calendar/month/SimpleWeekView.kt b/src/com/android/calendar/month/SimpleWeekView.kt index a71cff86..4d1298d4 100644 --- a/src/com/android/calendar/month/SimpleWeekView.kt +++ b/src/com/android/calendar/month/SimpleWeekView.kt @@ -49,17 +49,23 @@ open class SimpleWeekView(context: Context) : View(context) { @JvmField protected var mPadding = 0 @JvmField protected var r: Rect = Rect() @JvmField protected var p: Paint = Paint() - @JvmField protected var mMonthNumPaint: Paint? = null + @JvmField protected var mMonthNumPaint: Paint = Paint() @JvmField protected var mSelectedDayLine: Drawable // Cache the number strings so we don't have to recompute them each time @JvmField protected var mDayNumbers: Array? = null + // How many days to display + @JvmField protected var mNumDays = DEFAULT_NUM_DAYS + + // The number of days + a spot for week number if it is displayed + @JvmField protected var mNumCells = mNumDays + // Quick lookup for checking which days are in the focus month - @JvmField protected var mFocusDay: BooleanArray? = null + @JvmField protected var mFocusDay: BooleanArray = BooleanArray(mNumCells) // Quick lookup for checking which days are in an odd month (to set a different background) - @JvmField protected var mOddMonth: BooleanArray? = null + @JvmField protected var mOddMonth: BooleanArray = BooleanArray(mNumCells) // The Julian day of the first day displayed by this item @JvmField protected var mFirstJulianDay = -1 @@ -87,23 +93,17 @@ open class SimpleWeekView(context: Context) : View(context) { @JvmField protected var mHasSelectedDay = false // If this view contains the today - @JvmField protected var mHasToday = false + open protected var mHasToday = false // Which day is selected [0-6] or -1 if no day is selected @JvmField protected var mSelectedDay = DEFAULT_SELECTED_DAY // Which day is today [0-6] or -1 if no day is today - @JvmField protected var mToday = DEFAULT_SELECTED_DAY + @JvmField protected var mToday: Int = DEFAULT_SELECTED_DAY // Which day of the week to start on [0-6] @JvmField protected var mWeekStart = DEFAULT_WEEK_START - // How many days to display - @JvmField protected var mNumDays = DEFAULT_NUM_DAYS - - // The number of days + a spot for week number if it is displayed - @JvmField protected var mNumCells = mNumDays - // The left edge of the selected day @JvmField protected var mSelectedLeft = -1 @@ -140,25 +140,25 @@ open class SimpleWeekView(context: Context) : View(context) { mTimeZone = tz // We keep the current value for any params not present if (params.containsKey(VIEW_PARAMS_HEIGHT)) { - mHeight = params.get(VIEW_PARAMS_HEIGHT) as Int + mHeight = (params.get(VIEW_PARAMS_HEIGHT))!!.toInt() if (mHeight < MIN_HEIGHT) { mHeight = MIN_HEIGHT } } if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) { - mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY) as Int + mSelectedDay = (params.get(VIEW_PARAMS_SELECTED_DAY))!!.toInt() } mHasSelectedDay = mSelectedDay != -1 if (params.containsKey(VIEW_PARAMS_NUM_DAYS)) { - mNumDays = params.get(VIEW_PARAMS_NUM_DAYS) as Int + mNumDays = (params.get(VIEW_PARAMS_NUM_DAYS))!!.toInt() } if (params.containsKey(VIEW_PARAMS_SHOW_WK_NUM)) { mShowWeekNum = - if (params.get(VIEW_PARAMS_SHOW_WK_NUM) != 0) { - true - } else { - false - } + if (params.get(VIEW_PARAMS_SHOW_WK_NUM) != 0) { + true + } else { + false + } } mNumCells = if (mShowWeekNum) mNumDays + 1 else mNumDays @@ -166,7 +166,7 @@ open class SimpleWeekView(context: Context) : View(context) { mDayNumbers = arrayOfNulls(mNumCells) mFocusDay = BooleanArray(mNumCells) mOddMonth = BooleanArray(mNumCells) - mWeek = params.get(VIEW_PARAMS_WEEK) as Int + mWeek = (params.get(VIEW_PARAMS_WEEK))!!.toInt() val julianMonday: Int = Utils.getJulianMondayFromWeeksSinceEpoch(mWeek) val time = Time(tz) time.setJulianDay(julianMonday) @@ -178,7 +178,7 @@ open class SimpleWeekView(context: Context) : View(context) { i++ } if (params.containsKey(VIEW_PARAMS_WEEK_START)) { - mWeekStart = params.get(VIEW_PARAMS_WEEK_START) as Int + mWeekStart = (params.get(VIEW_PARAMS_WEEK_START))!!.toInt() } // Now adjust our starting day based on the start day of the week @@ -201,7 +201,7 @@ open class SimpleWeekView(context: Context) : View(context) { mHasToday = false mToday = -1 val focusMonth = if (params.containsKey(VIEW_PARAMS_FOCUS_MONTH)) params.get( - VIEW_PARAMS_FOCUS_MONTH + VIEW_PARAMS_FOCUS_MONTH ) else DEFAULT_FOCUS_MONTH while (i < mNumCells) { if (time.monthDay === 1) { @@ -286,7 +286,7 @@ open class SimpleWeekView(context: Context) : View(context) { */ open fun getDayFromLocation(x: Float): Time? { val dayStart = - if (mShowWeekNum) (mWidth - mPadding * 2) / mNumCells + mPadding else mPadding + if (mShowWeekNum) (mWidth - mPadding * 2) / mNumCells + mPadding else mPadding if (x < dayStart || x > mWidth - mPadding) { return null } @@ -373,7 +373,7 @@ open class SimpleWeekView(context: Context) : View(context) { } val x = (2 * i + 1) * (mWidth - mPadding * 2) / divisor + mPadding canvas.drawText(mDayNumbers!![i] as String, x.toFloat(), y.toFloat(), - mMonthNumPaint as Paint) + mMonthNumPaint as Paint) if (mHasToday && mToday == i) { mMonthNumPaint?.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE.toFloat()) mMonthNumPaint?.setFakeBoldText(false) @@ -426,9 +426,9 @@ open class SimpleWeekView(context: Context) : View(context) { selectedPosition++ } mSelectedLeft = (selectedPosition * (mWidth - mPadding * 2) / mNumCells + - mPadding) + mPadding) mSelectedRight = ((selectedPosition + 1) * (mWidth - mPadding * 2) / mNumCells + - mPadding) + mPadding) } } @@ -443,22 +443,22 @@ open class SimpleWeekView(context: Context) : View(context) { // only send accessibility events if accessibility and exploration are // on. val am: AccessibilityManager = context - .getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager + .getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager if (!am.isEnabled() || !am.isTouchExplorationEnabled()) { return super.onHoverEvent(event) } if (event.getAction() !== MotionEvent.ACTION_HOVER_EXIT) { val hover: Time? = getDayFromLocation(event.getX()) if (hover != null && - (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) !== 0) + (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) !== 0) ) { val millis: Long = hover.toMillis(true) val date: String? = Utils.formatDateRange( - context, millis, millis, - DateUtils.FORMAT_SHOW_DATE + context, millis, millis, + DateUtils.FORMAT_SHOW_DATE ) val accessEvent: AccessibilityEvent = - AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) accessEvent.getText().add(date) sendAccessibilityEventUnchecked(accessEvent) mLastHoverTime = hover @@ -530,7 +530,7 @@ open class SimpleWeekView(context: Context) : View(context) { protected var WEEK_NUM_MARGIN_BOTTOM = 4 // used for scaling to the device density - @JvmField protected var mScale = 0f + @JvmStatic protected var mScale = 0f } init { -- cgit v1.2.3 From f059a23e87cf4d4e72d0090faf17622618b5d3f5 Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Tue, 13 Jul 2021 19:57:32 +0000 Subject: AOSP/Calendar - .java files deleted Since the AOSP Calendar app has been fully converted to Kotlin, the .java files have been deleted. The app has gone through the general battery of tests such as unit, cts, and manual testing to ensure the app still functions properly. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/ testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=.......... Time: 0.162 OK (22 tests) General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 13m 38s Total aggregated tests run time: 13m 38s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.46 tests/sec [376 tests / 818070 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 10106 ms || clean = 2243 ms Total preparation time: 10s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 15m 16s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: I48d05571f02e77edac5e9522922d890f1adb2cf0 --- src/com/android/calendar/AllInOneActivity.java | 1062 ------ .../android/calendar/AsyncQueryServiceHelper.java | 70 - src/com/android/calendar/CalendarApplication.java | 32 - src/com/android/calendar/CalendarBackupAgent.java | 41 - src/com/android/calendar/CalendarController.java | 713 ---- src/com/android/calendar/CalendarData.java | 28 - src/com/android/calendar/CalendarUtils.java | 356 -- src/com/android/calendar/CalendarViewAdapter.java | 409 -- src/com/android/calendar/DayFragment.java | 256 -- src/com/android/calendar/DayOfMonthDrawable.java | 77 - src/com/android/calendar/DayView.java | 4008 -------------------- src/com/android/calendar/Event.java | 642 ---- src/com/android/calendar/EventGeometry.java | 170 - src/com/android/calendar/EventInfoActivity.java | 190 - src/com/android/calendar/EventInfoFragment.java | 877 ----- src/com/android/calendar/EventLoader.java | 286 -- src/com/android/calendar/GeneralPreferences.java | 400 -- .../calendar/GoogleCalendarUriIntentFilter.java | 41 - src/com/android/calendar/MultiStateButton.java | 192 - src/com/android/calendar/OtherPreferences.java | 210 - src/com/android/calendar/StickyHeaderListView.java | 395 -- src/com/android/calendar/UpgradeReceiver.java | 29 - src/com/android/calendar/Utils.java | 1499 -------- .../calendar/alerts/AlarmManagerInterface.java | 26 - .../android/calendar/alerts/AlarmScheduler.java | 322 -- src/com/android/calendar/alerts/AlertReceiver.java | 123 - src/com/android/calendar/alerts/AlertService.java | 202 - src/com/android/calendar/alerts/AlertUtils.java | 108 - .../calendar/alerts/DismissAlarmsService.java | 136 - .../calendar/alerts/GlobalDismissManager.java | 84 - .../android/calendar/alerts/InitAlarmsService.java | 62 - .../android/calendar/alerts/NotificationMgr.java | 41 - .../calendar/alerts/QuickResponseActivity.java | 108 - .../android/calendar/month/MonthByWeekAdapter.java | 406 -- .../calendar/month/MonthByWeekFragment.java | 494 --- src/com/android/calendar/month/MonthListView.java | 51 - .../calendar/month/MonthWeekEventsView.java | 1110 ------ .../calendar/month/SimpleDayPickerFragment.java | 612 --- src/com/android/calendar/month/SimpleWeekView.java | 551 --- .../android/calendar/month/SimpleWeeksAdapter.java | 302 -- .../calendar/widget/CalendarAppWidgetModel.java | 430 --- .../calendar/widget/CalendarAppWidgetProvider.java | 230 -- .../calendar/widget/CalendarAppWidgetService.java | 626 --- 43 files changed, 18007 deletions(-) delete mode 100644 src/com/android/calendar/AllInOneActivity.java delete mode 100644 src/com/android/calendar/AsyncQueryServiceHelper.java delete mode 100644 src/com/android/calendar/CalendarApplication.java delete mode 100644 src/com/android/calendar/CalendarBackupAgent.java delete mode 100644 src/com/android/calendar/CalendarController.java delete mode 100644 src/com/android/calendar/CalendarData.java delete mode 100644 src/com/android/calendar/CalendarUtils.java delete mode 100644 src/com/android/calendar/CalendarViewAdapter.java delete mode 100644 src/com/android/calendar/DayFragment.java delete mode 100644 src/com/android/calendar/DayOfMonthDrawable.java delete mode 100644 src/com/android/calendar/DayView.java delete mode 100644 src/com/android/calendar/Event.java delete mode 100644 src/com/android/calendar/EventGeometry.java delete mode 100644 src/com/android/calendar/EventInfoActivity.java delete mode 100644 src/com/android/calendar/EventInfoFragment.java delete mode 100644 src/com/android/calendar/EventLoader.java delete mode 100644 src/com/android/calendar/GeneralPreferences.java delete mode 100644 src/com/android/calendar/GoogleCalendarUriIntentFilter.java delete mode 100644 src/com/android/calendar/MultiStateButton.java delete mode 100644 src/com/android/calendar/OtherPreferences.java delete mode 100644 src/com/android/calendar/StickyHeaderListView.java delete mode 100644 src/com/android/calendar/UpgradeReceiver.java delete mode 100644 src/com/android/calendar/Utils.java delete mode 100644 src/com/android/calendar/alerts/AlarmManagerInterface.java delete mode 100644 src/com/android/calendar/alerts/AlarmScheduler.java delete mode 100644 src/com/android/calendar/alerts/AlertReceiver.java delete mode 100644 src/com/android/calendar/alerts/AlertService.java delete mode 100644 src/com/android/calendar/alerts/AlertUtils.java delete mode 100644 src/com/android/calendar/alerts/DismissAlarmsService.java delete mode 100644 src/com/android/calendar/alerts/GlobalDismissManager.java delete mode 100644 src/com/android/calendar/alerts/InitAlarmsService.java delete mode 100644 src/com/android/calendar/alerts/NotificationMgr.java delete mode 100644 src/com/android/calendar/alerts/QuickResponseActivity.java delete mode 100644 src/com/android/calendar/month/MonthByWeekAdapter.java delete mode 100644 src/com/android/calendar/month/MonthByWeekFragment.java delete mode 100644 src/com/android/calendar/month/MonthListView.java delete mode 100644 src/com/android/calendar/month/MonthWeekEventsView.java delete mode 100644 src/com/android/calendar/month/SimpleDayPickerFragment.java delete mode 100644 src/com/android/calendar/month/SimpleWeekView.java delete mode 100644 src/com/android/calendar/month/SimpleWeeksAdapter.java delete mode 100644 src/com/android/calendar/widget/CalendarAppWidgetModel.java delete mode 100644 src/com/android/calendar/widget/CalendarAppWidgetProvider.java delete mode 100644 src/com/android/calendar/widget/CalendarAppWidgetService.java diff --git a/src/com/android/calendar/AllInOneActivity.java b/src/com/android/calendar/AllInOneActivity.java deleted file mode 100644 index cec6a40f..00000000 --- a/src/com/android/calendar/AllInOneActivity.java +++ /dev/null @@ -1,1062 +0,0 @@ -/* - * Copyright (C) 2010 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.accounts.AccountManager; -import android.accounts.AccountManagerCallback; -import android.accounts.AccountManagerFuture; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.animation.ObjectAnimator; -import android.app.ActionBar; -import android.app.ActionBar.Tab; -import android.app.Activity; -import android.app.Fragment; -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.content.AsyncQueryHandler; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.database.Cursor; -import android.graphics.drawable.LayerDrawable; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.provider.CalendarContract; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.RelativeLayout.LayoutParams; -import android.widget.TextView; - -import com.android.calendar.CalendarController.EventHandler; -import com.android.calendar.CalendarController.EventInfo; -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.CalendarController.ViewType; -import com.android.calendar.month.MonthByWeekFragment; - -import java.io.IOException; -import java.util.List; -import java.util.Locale; -import java.util.TimeZone; - -import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; -import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; -import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; -import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; - -public class AllInOneActivity extends Activity implements EventHandler, - OnSharedPreferenceChangeListener, ActionBar.TabListener, - ActionBar.OnNavigationListener { - private static final String TAG = "AllInOneActivity"; - private static final boolean DEBUG = false; - private static final String EVENT_INFO_FRAGMENT_TAG = "EventInfoFragment"; - private static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time"; - private static final String BUNDLE_KEY_EVENT_ID = "key_event_id"; - private static final String BUNDLE_KEY_RESTORE_VIEW = "key_restore_view"; - private static final String BUNDLE_KEY_CHECK_ACCOUNTS = "key_check_for_accounts"; - private static final int HANDLER_KEY = 0; - - // Indices of buttons for the drop down menu (tabs replacement) - // Must match the strings in the array buttons_list in arrays.xml and the - // OnNavigationListener - private static final int BUTTON_DAY_INDEX = 0; - private static final int BUTTON_WEEK_INDEX = 1; - private static final int BUTTON_MONTH_INDEX = 2; - private static final int BUTTON_AGENDA_INDEX = 3; - - private CalendarController mController; - private static boolean mIsMultipane; - private static boolean mIsTabletConfig; - private boolean mOnSaveInstanceStateCalled = false; - private boolean mBackToPreviousView = false; - private ContentResolver mContentResolver; - private int mPreviousView; - private int mCurrentView; - private boolean mPaused = true; - private boolean mUpdateOnResume = false; - private boolean mHideControls = false; - private boolean mShowSideViews = true; - private boolean mShowWeekNum = false; - private TextView mHomeTime; - private TextView mDateRange; - private TextView mWeekTextView; - private View mMiniMonth; - private View mCalendarsList; - private View mMiniMonthContainer; - private View mSecondaryPane; - private String mTimeZone; - private boolean mShowCalendarControls; - private boolean mShowEventInfoFullScreen; - private int mWeekNum; - private int mCalendarControlsAnimationTime; - private int mControlsAnimateWidth; - private int mControlsAnimateHeight; - - private long mViewEventId = -1; - private long mIntentEventStartMillis = -1; - private long mIntentEventEndMillis = -1; - private int mIntentAttendeeResponse = Attendees.ATTENDEE_STATUS_NONE; - private boolean mIntentAllDay = false; - - // Action bar and Navigation bar (left side of Action bar) - private ActionBar mActionBar; - private ActionBar.Tab mDayTab; - private ActionBar.Tab mWeekTab; - private ActionBar.Tab mMonthTab; - private MenuItem mControlsMenu; - private Menu mOptionsMenu; - private CalendarViewAdapter mActionBarMenuSpinnerAdapter; - private QueryHandler mHandler; - private boolean mCheckForAccounts = true; - - private String mHideString; - private String mShowString; - - DayOfMonthDrawable mDayOfMonthIcon; - - int mOrientation; - - // Params for animating the controls on the right - private LayoutParams mControlsParams; - private LinearLayout.LayoutParams mVerticalControlsParams; - - private final AnimatorListener mSlideAnimationDoneListener = new AnimatorListener() { - - @Override - public void onAnimationCancel(Animator animation) { - } - - @Override - public void onAnimationEnd(android.animation.Animator animation) { - int visibility = mShowSideViews ? View.VISIBLE : View.GONE; - mMiniMonth.setVisibility(visibility); - mCalendarsList.setVisibility(visibility); - mMiniMonthContainer.setVisibility(visibility); - } - - @Override - public void onAnimationRepeat(android.animation.Animator animation) { - } - - @Override - public void onAnimationStart(android.animation.Animator animation) { - } - }; - - private class QueryHandler extends AsyncQueryHandler { - public QueryHandler(ContentResolver cr) { - super(cr); - } - - @Override - protected void onQueryComplete(int token, Object cookie, Cursor cursor) { - mCheckForAccounts = false; - try { - // If the query didn't return a cursor for some reason return - if (cursor == null || cursor.getCount() > 0 || isFinishing()) { - return; - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - Bundle options = new Bundle(); - options.putCharSequence("introMessage", - getResources().getString(R.string.create_an_account_desc)); - options.putBoolean("allowSkip", true); - - AccountManager am = AccountManager.get(AllInOneActivity.this); - am.addAccount("com.google", CalendarContract.AUTHORITY, null, options, - AllInOneActivity.this, - new AccountManagerCallback() { - @Override - public void run(AccountManagerFuture future) { - } - }, null); - } - } - - private final Runnable mHomeTimeUpdater = new Runnable() { - @Override - public void run() { - mTimeZone = Utils.getTimeZone(AllInOneActivity.this, mHomeTimeUpdater); - updateSecondaryTitleFields(-1); - AllInOneActivity.this.invalidateOptionsMenu(); - Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone); - } - }; - - // runs every midnight/time changes and refreshes the today icon - private final Runnable mTimeChangesUpdater = new Runnable() { - @Override - public void run() { - mTimeZone = Utils.getTimeZone(AllInOneActivity.this, mHomeTimeUpdater); - AllInOneActivity.this.invalidateOptionsMenu(); - Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone); - } - }; - - - // 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 true; - } - - @Override - public void onChange(boolean selfChange) { - eventsChanged(); - } - }; - - @Override - protected void onNewIntent(Intent intent) { - String action = intent.getAction(); - if (DEBUG) - Log.d(TAG, "New intent received " + intent.toString()); - // Don't change the date if we're just returning to the app's home - if (Intent.ACTION_VIEW.equals(action) - && !intent.getBooleanExtra(Utils.INTENT_KEY_HOME, false)) { - long millis = parseViewAction(intent); - if (millis == -1) { - millis = Utils.timeFromIntentInMillis(intent); - } - if (millis != -1 && mViewEventId == -1 && mController != null) { - Time time = new Time(mTimeZone); - time.set(millis); - time.normalize(true); - mController.sendEvent(this, EventType.GO_TO, time, time, -1, ViewType.CURRENT); - } - } - } - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - if (icicle != null && icicle.containsKey(BUNDLE_KEY_CHECK_ACCOUNTS)) { - mCheckForAccounts = icicle.getBoolean(BUNDLE_KEY_CHECK_ACCOUNTS); - } - // Launch add google account if this is first time and there are no - // accounts yet - if (mCheckForAccounts) { - - mHandler = new QueryHandler(this.getContentResolver()); - mHandler.startQuery(0, null, Calendars.CONTENT_URI, new String[] { - Calendars._ID - }, null, null /* selection args */, null /* sort order */); - } - - // This needs to be created before setContentView - mController = CalendarController.getInstance(this); - - - // Get time from intent or icicle - long timeMillis = -1; - int viewType = -1; - final Intent intent = getIntent(); - if (icicle != null) { - timeMillis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME); - viewType = icicle.getInt(BUNDLE_KEY_RESTORE_VIEW, -1); - } else { - String action = intent.getAction(); - if (Intent.ACTION_VIEW.equals(action)) { - // Open EventInfo later - timeMillis = parseViewAction(intent); - } - - if (timeMillis == -1) { - timeMillis = Utils.timeFromIntentInMillis(intent); - } - } - - if (viewType == -1 || viewType > ViewType.MAX_VALUE) { - viewType = Utils.getViewTypeFromIntentAndSharedPref(this); - } - mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater); - Time t = new Time(mTimeZone); - t.set(timeMillis); - - if (DEBUG) { - if (icicle != null && intent != null) { - Log.d(TAG, "both, icicle:" + icicle.toString() + " intent:" + intent.toString()); - } else { - Log.d(TAG, "not both, icicle:" + icicle + " intent:" + intent); - } - } - - Resources res = getResources(); - mHideString = res.getString(R.string.hide_controls); - mShowString = res.getString(R.string.show_controls); - mOrientation = res.getConfiguration().orientation; - if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) { - mControlsAnimateWidth = (int)res.getDimension(R.dimen.calendar_controls_width); - if (mControlsParams == null) { - mControlsParams = new LayoutParams(mControlsAnimateWidth, 0); - } - mControlsParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - } else { - // Make sure width is in between allowed min and max width values - mControlsAnimateWidth = Math.max(res.getDisplayMetrics().widthPixels * 45 / 100, - (int)res.getDimension(R.dimen.min_portrait_calendar_controls_width)); - mControlsAnimateWidth = Math.min(mControlsAnimateWidth, - (int)res.getDimension(R.dimen.max_portrait_calendar_controls_width)); - } - - mControlsAnimateHeight = (int)res.getDimension(R.dimen.calendar_controls_height); - - mHideControls = true; - mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config); - mIsTabletConfig = Utils.getConfigBool(this, R.bool.tablet_config); - mShowCalendarControls = - Utils.getConfigBool(this, R.bool.show_calendar_controls); - mShowEventInfoFullScreen = - Utils.getConfigBool(this, R.bool.show_event_info_full_screen); - mCalendarControlsAnimationTime = res.getInteger(R.integer.calendar_controls_animation_time); - Utils.setAllowWeekForDetailView(mIsMultipane); - - // setContentView must be called before configureActionBar - setContentView(R.layout.all_in_one); - - if (mIsTabletConfig) { - mDateRange = (TextView) findViewById(R.id.date_bar); - mWeekTextView = (TextView) findViewById(R.id.week_num); - } else { - mDateRange = (TextView) getLayoutInflater().inflate(R.layout.date_range_title, null); - } - - // configureActionBar auto-selects the first tab you add, so we need to - // call it before we set up our own fragments to make sure it doesn't - // overwrite us - configureActionBar(viewType); - - mHomeTime = (TextView) findViewById(R.id.home_time); - mMiniMonth = findViewById(R.id.mini_month); - if (mIsTabletConfig && mOrientation == Configuration.ORIENTATION_PORTRAIT) { - mMiniMonth.setLayoutParams(new RelativeLayout.LayoutParams(mControlsAnimateWidth, - mControlsAnimateHeight)); - } - mCalendarsList = findViewById(R.id.calendar_list); - mMiniMonthContainer = findViewById(R.id.mini_month_container); - mSecondaryPane = findViewById(R.id.secondary_pane); - - // Must register as the first activity because this activity can modify - // the list of event handlers in it's handle method. This affects who - // the rest of the handlers the controller dispatches to are. - mController.registerFirstEventHandler(HANDLER_KEY, this); - - initFragments(timeMillis, viewType, icicle); - - // Listen for changes that would require this to be refreshed - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); - prefs.registerOnSharedPreferenceChangeListener(this); - - mContentResolver = getContentResolver(); - } - - private long parseViewAction(final Intent intent) { - long timeMillis = -1; - Uri data = intent.getData(); - if (data != null && data.isHierarchical()) { - List path = data.getPathSegments(); - if (path.size() == 2 && path.get(0).equals("events")) { - try { - mViewEventId = Long.valueOf(data.getLastPathSegment()); - if (mViewEventId != -1) { - mIntentEventStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0); - mIntentEventEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0); - mIntentAttendeeResponse = intent.getIntExtra( - ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE); - mIntentAllDay = intent.getBooleanExtra(EXTRA_EVENT_ALL_DAY, false); - timeMillis = mIntentEventStartMillis; - } - } catch (NumberFormatException e) { - // Ignore if mViewEventId can't be parsed - } - } - } - return timeMillis; - } - - private void configureActionBar(int viewType) { - createButtonsSpinner(viewType, mIsTabletConfig); - if (mIsMultipane) { - mActionBar.setDisplayOptions( - ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME); - } else { - mActionBar.setDisplayOptions(0); - } - } - - private void createButtonsSpinner(int viewType, boolean tabletConfig) { - // If tablet configuration , show spinner with no dates - mActionBarMenuSpinnerAdapter = new CalendarViewAdapter (this, viewType, !tabletConfig); - mActionBar = getActionBar(); - mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); - mActionBar.setListNavigationCallbacks(mActionBarMenuSpinnerAdapter, this); - switch (viewType) { - case ViewType.AGENDA: - break; - case ViewType.DAY: - mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX); - break; - case ViewType.WEEK: - mActionBar.setSelectedNavigationItem(BUTTON_WEEK_INDEX); - break; - case ViewType.MONTH: - mActionBar.setSelectedNavigationItem(BUTTON_MONTH_INDEX); - break; - default: - mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX); - break; - } - } - // Clear buttons used in the agenda view - private void clearOptionsMenu() { - if (mOptionsMenu == null) { - return; - } - MenuItem cancelItem = mOptionsMenu.findItem(R.id.action_cancel); - if (cancelItem != null) { - cancelItem.setVisible(false); - } - } - - @Override - protected void onResume() { - super.onResume(); - - // Check if the upgrade code has ever been run. If not, force a sync just this one time. - Utils.trySyncAndDisableUpgradeReceiver(this); - - // Must register as the first activity because this activity can modify - // the list of event handlers in it's handle method. This affects who - // the rest of the handlers the controller dispatches to are. - mController.registerFirstEventHandler(HANDLER_KEY, this); - - mOnSaveInstanceStateCalled = false; - mContentResolver.registerContentObserver(CalendarContract.Events.CONTENT_URI, - true, mObserver); - if (mUpdateOnResume) { - initFragments(mController.getTime(), mController.getViewType(), null); - mUpdateOnResume = false; - } - Time t = new Time(mTimeZone); - t.set(mController.getTime()); - mController.sendEvent(this, EventType.UPDATE_TITLE, t, t, -1, ViewType.CURRENT, - mController.getDateFlags(), null, null); - // Make sure the drop-down menu will get its date updated at midnight - if (mActionBarMenuSpinnerAdapter != null) { - mActionBarMenuSpinnerAdapter.refresh(this); - } - - if (mControlsMenu != null) { - mControlsMenu.setTitle(mHideControls ? mShowString : mHideString); - } - mPaused = false; - - if (mViewEventId != -1 && mIntentEventStartMillis != -1 && mIntentEventEndMillis != -1) { - long currentMillis = System.currentTimeMillis(); - long selectedTime = -1; - if (currentMillis > mIntentEventStartMillis && currentMillis < mIntentEventEndMillis) { - selectedTime = currentMillis; - } - mController.sendEventRelatedEventWithExtra(this, EventType.VIEW_EVENT, mViewEventId, - mIntentEventStartMillis, mIntentEventEndMillis, -1, -1, - EventInfo.buildViewExtraLong(mIntentAttendeeResponse,mIntentAllDay), - selectedTime); - mViewEventId = -1; - mIntentEventStartMillis = -1; - mIntentEventEndMillis = -1; - mIntentAllDay = false; - } - Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone); - // Make sure the today icon is up to date - invalidateOptionsMenu(); - } - - @Override - protected void onPause() { - super.onPause(); - - mController.deregisterEventHandler(HANDLER_KEY); - mPaused = true; - mHomeTime.removeCallbacks(mHomeTimeUpdater); - if (mActionBarMenuSpinnerAdapter != null) { - mActionBarMenuSpinnerAdapter.onPause(); - } - mContentResolver.unregisterContentObserver(mObserver); - if (isFinishing()) { - // Stop listening for changes that would require this to be refreshed - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); - prefs.unregisterOnSharedPreferenceChangeListener(this); - } - // FRAG_TODO save highlighted days of the week; - if (mController.getViewType() != ViewType.EDIT) { - Utils.setDefaultView(this, mController.getViewType()); - } - Utils.resetMidnightUpdater(mHandler, mTimeChangesUpdater); - } - - @Override - protected void onUserLeaveHint() { - mController.sendEvent(this, EventType.USER_HOME, null, null, -1, ViewType.CURRENT); - super.onUserLeaveHint(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - mOnSaveInstanceStateCalled = true; - super.onSaveInstanceState(outState); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this); - prefs.unregisterOnSharedPreferenceChangeListener(this); - - mController.deregisterAllEventHandlers(); - - CalendarController.removeInstance(this); - } - - private void initFragments(long timeMillis, int viewType, Bundle icicle) { - if (DEBUG) { - Log.d(TAG, "Initializing to " + timeMillis + " for view " + viewType); - } - FragmentTransaction ft = getFragmentManager().beginTransaction(); - - if (mShowCalendarControls) { - Fragment miniMonthFrag = new MonthByWeekFragment(timeMillis, true); - ft.replace(R.id.mini_month, miniMonthFrag); - mController.registerEventHandler(R.id.mini_month, (EventHandler) miniMonthFrag); - } - if (!mShowCalendarControls || viewType == ViewType.EDIT) { - mMiniMonth.setVisibility(View.GONE); - mCalendarsList.setVisibility(View.GONE); - } - - EventInfo info = null; - if (viewType == ViewType.EDIT) { - mPreviousView = GeneralPreferences.getSharedPreferences(this).getInt( - GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW); - - long eventId = -1; - Intent intent = getIntent(); - Uri data = intent.getData(); - if (data != null) { - try { - eventId = Long.parseLong(data.getLastPathSegment()); - } catch (NumberFormatException e) { - if (DEBUG) { - Log.d(TAG, "Create new event"); - } - } - } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) { - eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID); - } - - long begin = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1); - long end = intent.getLongExtra(EXTRA_EVENT_END_TIME, -1); - info = new EventInfo(); - if (end != -1) { - info.endTime = new Time(); - info.endTime.set(end); - } - if (begin != -1) { - info.startTime = new Time(); - info.startTime.set(begin); - } - info.id = eventId; - // We set the viewtype so if the user presses back when they are - // done editing the controller knows we were in the Edit Event - // screen. Likewise for eventId - mController.setViewType(viewType); - mController.setEventId(eventId); - } else { - mPreviousView = viewType; - } - - setMainPane(ft, R.id.main_pane, viewType, timeMillis, true); - ft.commit(); // this needs to be after setMainPane() - - Time t = new Time(mTimeZone); - t.set(timeMillis); - if (viewType != ViewType.EDIT) { - mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType); - } - } - - @Override - public void onBackPressed() { - if (mCurrentView == ViewType.EDIT || mBackToPreviousView) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, mPreviousView); - } else { - super.onBackPressed(); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - mOptionsMenu = menu; - getMenuInflater().inflate(R.menu.all_in_one_title_bar, menu); - - // Hide the "show/hide controls" button if this is a phone - // or the view type is "Month". - - mControlsMenu = menu.findItem(R.id.action_hide_controls); - if (!mShowCalendarControls) { - if (mControlsMenu != null) { - mControlsMenu.setVisible(false); - mControlsMenu.setEnabled(false); - } - } else if (mControlsMenu != null && mController != null - && (mController.getViewType() == ViewType.MONTH)) { - mControlsMenu.setVisible(false); - mControlsMenu.setEnabled(false); - } else if (mControlsMenu != null){ - mControlsMenu.setTitle(mHideControls ? mShowString : mHideString); - } - - MenuItem menuItem = menu.findItem(R.id.action_today); - if (Utils.isJellybeanOrLater()) { - // replace the default top layer drawable of the today icon with a - // custom drawable that shows the day of the month of today - LayerDrawable icon = (LayerDrawable) menuItem.getIcon(); - Utils.setTodayIcon(icon, this, mTimeZone); - } else { - menuItem.setIcon(R.drawable.ic_menu_today_no_date_holo_light); - } - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - Time t = null; - int viewType = ViewType.CURRENT; - long extras = CalendarController.EXTRA_GOTO_TIME; - final int itemId = item.getItemId(); - if (itemId == R.id.action_today) { - viewType = ViewType.CURRENT; - t = new Time(mTimeZone); - t.setToNow(); - extras |= CalendarController.EXTRA_GOTO_TODAY; - } else if (itemId == R.id.action_hide_controls) { - mHideControls = !mHideControls; - item.setTitle(mHideControls ? mShowString : mHideString); - if (!mHideControls) { - mMiniMonth.setVisibility(View.VISIBLE); - mCalendarsList.setVisibility(View.VISIBLE); - mMiniMonthContainer.setVisibility(View.VISIBLE); - } - final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this, "controlsOffset", - mHideControls ? 0 : mControlsAnimateWidth, - mHideControls ? mControlsAnimateWidth : 0); - slideAnimation.setDuration(mCalendarControlsAnimationTime); - ObjectAnimator.setFrameDelay(0); - slideAnimation.start(); - return true; - } else { - Log.d(TAG, "Unsupported itemId: " + itemId); - return true; - } - mController.sendEvent(this, EventType.GO_TO, t, null, t, -1, viewType, extras, null, null); - return true; - } - - /** - * Sets the offset of the controls on the right for animating them off/on - * screen. ProGuard strips this if it's not in proguard.flags - * - * @param controlsOffset The current offset in pixels - */ - public void setControlsOffset(int controlsOffset) { - if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) { - mMiniMonth.setTranslationX(controlsOffset); - mCalendarsList.setTranslationX(controlsOffset); - mControlsParams.width = Math.max(0, mControlsAnimateWidth - controlsOffset); - mMiniMonthContainer.setLayoutParams(mControlsParams); - } else { - mMiniMonth.setTranslationY(controlsOffset); - mCalendarsList.setTranslationY(controlsOffset); - if (mVerticalControlsParams == null) { - mVerticalControlsParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, mControlsAnimateHeight); - } - mVerticalControlsParams.height = Math.max(0, mControlsAnimateHeight - controlsOffset); - mMiniMonthContainer.setLayoutParams(mVerticalControlsParams); - } - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { - if (key.equals(GeneralPreferences.KEY_WEEK_START_DAY)) { - if (mPaused) { - mUpdateOnResume = true; - } else { - initFragments(mController.getTime(), mController.getViewType(), null); - } - } - } - - private void setMainPane( - FragmentTransaction ft, int viewId, int viewType, long timeMillis, boolean force) { - if (mOnSaveInstanceStateCalled) { - return; - } - if (!force && mCurrentView == viewType) { - return; - } - - // Remove this when transition to and from month view looks fine. - boolean doTransition = viewType != ViewType.MONTH && mCurrentView != ViewType.MONTH; - FragmentManager fragmentManager = getFragmentManager(); - - if (viewType != mCurrentView) { - // The rules for this previous view are different than the - // controller's and are used for intercepting the back button. - if (mCurrentView != ViewType.EDIT && mCurrentView > 0) { - mPreviousView = mCurrentView; - } - mCurrentView = viewType; - } - // Create new fragment - Fragment frag = null; - Fragment secFrag = null; - switch (viewType) { - case ViewType.AGENDA: - break; - case ViewType.DAY: - if (mActionBar != null && (mActionBar.getSelectedTab() != mDayTab)) { - mActionBar.selectTab(mDayTab); - } - if (mActionBarMenuSpinnerAdapter != null) { - mActionBar.setSelectedNavigationItem(CalendarViewAdapter.DAY_BUTTON_INDEX); - } - frag = new DayFragment(timeMillis, 1); - break; - case ViewType.MONTH: - if (mActionBar != null && (mActionBar.getSelectedTab() != mMonthTab)) { - mActionBar.selectTab(mMonthTab); - } - if (mActionBarMenuSpinnerAdapter != null) { - mActionBar.setSelectedNavigationItem(CalendarViewAdapter.MONTH_BUTTON_INDEX); - } - frag = new MonthByWeekFragment(timeMillis, false); - break; - case ViewType.WEEK: - default: - if (mActionBar != null && (mActionBar.getSelectedTab() != mWeekTab)) { - mActionBar.selectTab(mWeekTab); - } - if (mActionBarMenuSpinnerAdapter != null) { - mActionBar.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX); - } - frag = new DayFragment(timeMillis, 7); - break; - } - - // Update the current view so that the menu can update its look according to the - // current view. - if (mActionBarMenuSpinnerAdapter != null) { - mActionBarMenuSpinnerAdapter.setMainView(viewType); - if (!mIsTabletConfig) { - mActionBarMenuSpinnerAdapter.setTime(timeMillis); - } - } - - - // Show date only on tablet configurations in views different than Agenda - if (!mIsTabletConfig) { - mDateRange.setVisibility(View.GONE); - } else { - mDateRange.setVisibility(View.GONE); - } - - // Clear unnecessary buttons from the option menu when switching from the agenda view - if (viewType != ViewType.AGENDA) { - clearOptionsMenu(); - } - - boolean doCommit = false; - if (ft == null) { - doCommit = true; - ft = fragmentManager.beginTransaction(); - } - - if (doTransition) { - ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); - } - - ft.replace(viewId, frag); - if (DEBUG) { - Log.d(TAG, "Adding handler with viewId " + viewId + " and type " + viewType); - } - // If the key is already registered this will replace it - mController.registerEventHandler(viewId, (EventHandler) frag); - - if (doCommit) { - if (DEBUG) { - Log.d(TAG, "setMainPane AllInOne=" + this + " finishing:" + this.isFinishing()); - } - ft.commit(); - } - } - - private void setTitleInActionBar(EventInfo event) { - if (event.eventType != EventType.UPDATE_TITLE || mActionBar == null) { - return; - } - - final long start = event.startTime.toMillis(false /* use isDst */); - final long end; - if (event.endTime != null) { - end = event.endTime.toMillis(false /* use isDst */); - } else { - end = start; - } - - final String msg = Utils.formatDateRange(this, start, end, (int) event.extraLong); - CharSequence oldDate = mDateRange.getText(); - mDateRange.setText(msg); - updateSecondaryTitleFields(event.selectedTime != null ? event.selectedTime.toMillis(true) - : start); - if (!TextUtils.equals(oldDate, msg)) { - mDateRange.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); - if (mShowWeekNum && mWeekTextView != null) { - mWeekTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); - } - } - } - - private void updateSecondaryTitleFields(long visibleMillisSinceEpoch) { - mShowWeekNum = Utils.getShowWeekNumber(this); - mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater); - if (visibleMillisSinceEpoch != -1) { - int weekNum = Utils.getWeekNumberFromTime(visibleMillisSinceEpoch, this); - mWeekNum = weekNum; - } - - if (mShowWeekNum && (mCurrentView == ViewType.WEEK) && mIsTabletConfig - && mWeekTextView != null) { - String weekString = getResources().getQuantityString(R.plurals.weekN, mWeekNum, - mWeekNum); - mWeekTextView.setText(weekString); - mWeekTextView.setVisibility(View.VISIBLE); - } else if (visibleMillisSinceEpoch != -1 && mWeekTextView != null - && mCurrentView == ViewType.DAY && mIsTabletConfig) { - Time time = new Time(mTimeZone); - time.set(visibleMillisSinceEpoch); - int julianDay = Time.getJulianDay(visibleMillisSinceEpoch, time.gmtoff); - time.setToNow(); - int todayJulianDay = Time.getJulianDay(time.toMillis(false), time.gmtoff); - String dayString = Utils.getDayOfWeekString(julianDay, todayJulianDay, - visibleMillisSinceEpoch, this); - mWeekTextView.setText(dayString); - mWeekTextView.setVisibility(View.VISIBLE); - } else if (mWeekTextView != null && (!mIsTabletConfig || mCurrentView != ViewType.DAY)) { - mWeekTextView.setVisibility(View.GONE); - } - - if (mHomeTime != null - && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK) - && !TextUtils.equals(mTimeZone, Time.getCurrentTimezone())) { - Time time = new Time(mTimeZone); - time.setToNow(); - long millis = time.toMillis(true); - boolean isDST = time.isDst != 0; - int flags = DateUtils.FORMAT_SHOW_TIME; - if (DateFormat.is24HourFormat(this)) { - flags |= DateUtils.FORMAT_24HOUR; - } - // Formats the time as - String timeString = (new StringBuilder( - Utils.formatDateRange(this, millis, millis, flags))).append(" ").append( - TimeZone.getTimeZone(mTimeZone).getDisplayName( - isDST, TimeZone.SHORT, Locale.getDefault())).toString(); - mHomeTime.setText(timeString); - mHomeTime.setVisibility(View.VISIBLE); - // Update when the minute changes - mHomeTime.removeCallbacks(mHomeTimeUpdater); - mHomeTime.postDelayed( - mHomeTimeUpdater, - DateUtils.MINUTE_IN_MILLIS - (millis % DateUtils.MINUTE_IN_MILLIS)); - } else if (mHomeTime != null) { - mHomeTime.setVisibility(View.GONE); - } - } - - @Override - public long getSupportedEventTypes() { - return EventType.GO_TO | EventType.UPDATE_TITLE; - } - - @Override - public void handleEvent(EventInfo event) { - long displayTime = -1; - if (event.eventType == EventType.GO_TO) { - if ((event.extraLong & CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS) != 0) { - mBackToPreviousView = true; - } else if (event.viewType != mController.getPreviousViewType() - && event.viewType != ViewType.EDIT) { - // Clear the flag is change to a different view type - mBackToPreviousView = false; - } - - setMainPane( - null, R.id.main_pane, event.viewType, event.startTime.toMillis(false), false); - if (mShowCalendarControls) { - int animationSize = (mOrientation == Configuration.ORIENTATION_LANDSCAPE) ? - mControlsAnimateWidth : mControlsAnimateHeight; - boolean noControlsView = event.viewType == ViewType.MONTH; - if (mControlsMenu != null) { - mControlsMenu.setVisible(!noControlsView); - mControlsMenu.setEnabled(!noControlsView); - } - if (noControlsView || mHideControls) { - // hide minimonth and calendar frag - mShowSideViews = false; - if (!mHideControls) { - final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this, - "controlsOffset", 0, animationSize); - slideAnimation.addListener(mSlideAnimationDoneListener); - slideAnimation.setDuration(mCalendarControlsAnimationTime); - ObjectAnimator.setFrameDelay(0); - slideAnimation.start(); - } else { - mMiniMonth.setVisibility(View.GONE); - mCalendarsList.setVisibility(View.GONE); - mMiniMonthContainer.setVisibility(View.GONE); - } - } else { - // show minimonth and calendar frag - mShowSideViews = true; - mMiniMonth.setVisibility(View.VISIBLE); - mCalendarsList.setVisibility(View.VISIBLE); - mMiniMonthContainer.setVisibility(View.VISIBLE); - if (!mHideControls && - (mController.getPreviousViewType() == ViewType.MONTH)) { - final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this, - "controlsOffset", animationSize, 0); - slideAnimation.setDuration(mCalendarControlsAnimationTime); - ObjectAnimator.setFrameDelay(0); - slideAnimation.start(); - } - } - } - displayTime = event.selectedTime != null ? event.selectedTime.toMillis(true) - : event.startTime.toMillis(true); - if (!mIsTabletConfig) { - mActionBarMenuSpinnerAdapter.setTime(displayTime); - } - } else if (event.eventType == EventType.UPDATE_TITLE) { - setTitleInActionBar(event); - if (!mIsTabletConfig) { - mActionBarMenuSpinnerAdapter.setTime(mController.getTime()); - } - } - updateSecondaryTitleFields(displayTime); - } - - @Override - public void eventsChanged() { - mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT); - } - - @Override - public void onTabSelected(Tab tab, FragmentTransaction ft) { - Log.w(TAG, "TabSelected AllInOne=" + this + " finishing:" + this.isFinishing()); - if (tab == mDayTab && mCurrentView != ViewType.DAY) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY); - } else if (tab == mWeekTab && mCurrentView != ViewType.WEEK) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK); - } else if (tab == mMonthTab && mCurrentView != ViewType.MONTH) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH); - } else { - Log.w(TAG, "TabSelected event from unknown tab: " - + (tab == null ? "null" : tab.getText())); - Log.w(TAG, "CurrentView:" + mCurrentView + " Tab:" + tab.toString() + " Day:" + mDayTab - + " Week:" + mWeekTab + " Month:" + mMonthTab); - } - } - - @Override - public void onTabReselected(Tab tab, FragmentTransaction ft) { - } - - @Override - public void onTabUnselected(Tab tab, FragmentTransaction ft) { - } - - - @Override - public boolean onNavigationItemSelected(int itemPosition, long itemId) { - switch (itemPosition) { - case CalendarViewAdapter.DAY_BUTTON_INDEX: - if (mCurrentView != ViewType.DAY) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY); - } - break; - case CalendarViewAdapter.WEEK_BUTTON_INDEX: - if (mCurrentView != ViewType.WEEK) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK); - } - break; - case CalendarViewAdapter.MONTH_BUTTON_INDEX: - if (mCurrentView != ViewType.MONTH) { - mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH); - } - break; - case CalendarViewAdapter.AGENDA_BUTTON_INDEX: - break; - default: - Log.w(TAG, "ItemSelected event from unknown button: " + itemPosition); - Log.w(TAG, "CurrentView:" + mCurrentView + " Button:" + itemPosition + - " Day:" + mDayTab + " Week:" + mWeekTab + " Month:" + mMonthTab); - break; - } - return false; - } -} diff --git a/src/com/android/calendar/AsyncQueryServiceHelper.java b/src/com/android/calendar/AsyncQueryServiceHelper.java deleted file mode 100644 index c6e0a2bc..00000000 --- a/src/com/android/calendar/AsyncQueryServiceHelper.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2010 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.app.IntentService; -import android.content.ContentProviderOperation; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.OperationApplicationException; -import android.database.Cursor; -import android.net.Uri; -import android.os.Handler; -import android.os.Message; -import android.os.RemoteException; -import android.os.SystemClock; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.PriorityQueue; -import java.util.concurrent.Delayed; -import java.util.concurrent.TimeUnit; - -public class AsyncQueryServiceHelper extends IntentService { - private static final String TAG = "AsyncQuery"; - - public AsyncQueryServiceHelper(String name) { - super(name); - } - - public AsyncQueryServiceHelper() { - super("AsyncQueryServiceHelper"); - } - - @Override - protected void onHandleIntent(Intent intent) { - } - - @Override - public void onStart(Intent intent, int startId) { - super.onStart(intent, startId); - } - - @Override - public void onCreate() { - super.onCreate(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - } -} diff --git a/src/com/android/calendar/CalendarApplication.java b/src/com/android/calendar/CalendarApplication.java deleted file mode 100644 index d0ca4698..00000000 --- a/src/com/android/calendar/CalendarApplication.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.app.Application; - -public class CalendarApplication extends Application { - @Override - public void onCreate() { - super.onCreate(); - - /* - * Ensure the default values are set for any receiver, activity, - * service, etc. of Calendar - */ - GeneralPreferences.setDefaultValues(this); - } -} diff --git a/src/com/android/calendar/CalendarBackupAgent.java b/src/com/android/calendar/CalendarBackupAgent.java deleted file mode 100644 index 02456fdc..00000000 --- a/src/com/android/calendar/CalendarBackupAgent.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2010 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.app.backup.BackupAgentHelper; -import android.app.backup.BackupDataInput; -import android.app.backup.SharedPreferencesBackupHelper; -import android.content.Context; -import android.content.SharedPreferences.Editor; -import android.os.ParcelFileDescriptor; - -import java.io.IOException; - -public class CalendarBackupAgent extends BackupAgentHelper -{ - static final String SHARED_KEY = "shared_pref"; - - @Override - public void onCreate() { - } - - @Override - public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) - throws IOException { - super.onRestore(data, appVersionCode, newState); - } -} diff --git a/src/com/android/calendar/CalendarController.java b/src/com/android/calendar/CalendarController.java deleted file mode 100644 index 37286f2e..00000000 --- a/src/com/android/calendar/CalendarController.java +++ /dev/null @@ -1,713 +0,0 @@ -/* - * Copyright (C) 2010 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 static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; -import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; -import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; -import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.Activity; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.text.format.Time; -import android.util.Log; -import android.util.Pair; - -import java.lang.ref.WeakReference; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.Map.Entry; -import java.util.WeakHashMap; - -public class CalendarController { - private static final boolean DEBUG = false; - private static final String TAG = "CalendarController"; - - public static final String EVENT_EDIT_ON_LAUNCH = "editMode"; - - public static final int MIN_CALENDAR_YEAR = 1970; - public static final int MAX_CALENDAR_YEAR = 2036; - public static final int MIN_CALENDAR_WEEK = 0; - public static final int MAX_CALENDAR_WEEK = 3497; // weeks between 1/1/1970 and 1/1/2037 - - private final Context mContext; - - // This uses a LinkedHashMap so that we can replace fragments based on the - // view id they are being expanded into since we can't guarantee a reference - // to the handler will be findable - private final LinkedHashMap eventHandlers = - new LinkedHashMap(5); - private final LinkedList mToBeRemovedEventHandlers = new LinkedList(); - private final LinkedHashMap mToBeAddedEventHandlers = new LinkedHashMap< - Integer, EventHandler>(); - private Pair mFirstEventHandler; - private Pair mToBeAddedFirstEventHandler; - private volatile int mDispatchInProgressCounter = 0; - - private static WeakHashMap> instances = - new WeakHashMap>(); - - private final WeakHashMap filters = new WeakHashMap(1); - - private int mViewType = -1; - private int mDetailViewType = -1; - private int mPreviousViewType = -1; - private long mEventId = -1; - private final Time mTime = new Time(); - private long mDateFlags = 0; - - private final Runnable mUpdateTimezone = new Runnable() { - @Override - public void run() { - mTime.switchTimezone(Utils.getTimeZone(mContext, this)); - } - }; - - /** - * One of the event types that are sent to or from the controller - */ - public interface EventType { - // Simple view of an event - final long VIEW_EVENT = 1L << 1; - - // Full detail view in read only mode - final long VIEW_EVENT_DETAILS = 1L << 2; - - // full detail view in edit mode - final long EDIT_EVENT = 1L << 3; - - final long GO_TO = 1L << 5; - - final long EVENTS_CHANGED = 1L << 7; - - final long USER_HOME = 1L << 9; - - // date range has changed, update the title - final long UPDATE_TITLE = 1L << 10; - } - - /** - * One of the Agenda/Day/Week/Month view types - */ - public interface ViewType { - final int DETAIL = -1; - final int CURRENT = 0; - final int AGENDA = 1; - final int DAY = 2; - final int WEEK = 3; - final int MONTH = 4; - final int EDIT = 5; - final int MAX_VALUE = 5; - } - - public static class EventInfo { - - private static final long ATTENTEE_STATUS_MASK = 0xFF; - private static final long ALL_DAY_MASK = 0x100; - private static final int ATTENDEE_STATUS_NONE_MASK = 0x01; - private static final int ATTENDEE_STATUS_ACCEPTED_MASK = 0x02; - private static final int ATTENDEE_STATUS_DECLINED_MASK = 0x04; - private static final int ATTENDEE_STATUS_TENTATIVE_MASK = 0x08; - - public long eventType; // one of the EventType - public int viewType; // one of the ViewType - public long id; // event id - public Time selectedTime; // the selected time in focus - - // Event start and end times. All-day events are represented in: - // - local time for GO_TO commands - // - UTC time for VIEW_EVENT and other event-related commands - public Time startTime; - public Time endTime; - - public int x; // x coordinate in the activity space - public int y; // y coordinate in the activity space - public String query; // query for a user search - public ComponentName componentName; // used in combination with query - public String eventTitle; - public long calendarId; - - /** - * For EventType.VIEW_EVENT: - * It is the default attendee response and an all day event indicator. - * Set to Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED, - * Attendees.ATTENDEE_STATUS_DECLINED, or Attendees.ATTENDEE_STATUS_TENTATIVE. - * To signal the event is an all-day event, "or" ALL_DAY_MASK with the response. - * Alternatively, use buildViewExtraLong(), getResponse(), and isAllDay(). - *

- * For EventType.GO_TO: - * Set to {@link #EXTRA_GOTO_TIME} to go to the specified date/time. - * Set to {@link #EXTRA_GOTO_DATE} to consider the date but ignore the time. - * Set to {@link #EXTRA_GOTO_BACK_TO_PREVIOUS} if back should bring back previous view. - * Set to {@link #EXTRA_GOTO_TODAY} if this is a user request to go to the current time. - *

- * For EventType.UPDATE_TITLE: - * Set formatting flags for Utils.formatDateRange - */ - public long extraLong; - - public boolean isAllDay() { - if (eventType != EventType.VIEW_EVENT) { - Log.wtf(TAG, "illegal call to isAllDay , wrong event type " + eventType); - return false; - } - return ((extraLong & ALL_DAY_MASK) != 0) ? true : false; - } - - public int getResponse() { - if (eventType != EventType.VIEW_EVENT) { - Log.wtf(TAG, "illegal call to getResponse , wrong event type " + eventType); - return Attendees.ATTENDEE_STATUS_NONE; - } - - int response = (int)(extraLong & ATTENTEE_STATUS_MASK); - switch (response) { - case ATTENDEE_STATUS_NONE_MASK: - return Attendees.ATTENDEE_STATUS_NONE; - case ATTENDEE_STATUS_ACCEPTED_MASK: - return Attendees.ATTENDEE_STATUS_ACCEPTED; - case ATTENDEE_STATUS_DECLINED_MASK: - return Attendees.ATTENDEE_STATUS_DECLINED; - case ATTENDEE_STATUS_TENTATIVE_MASK: - return Attendees.ATTENDEE_STATUS_TENTATIVE; - default: - Log.wtf(TAG,"Unknown attendee response " + response); - } - return ATTENDEE_STATUS_NONE_MASK; - } - - // Used to build the extra long for a VIEW event. - public static long buildViewExtraLong(int response, boolean allDay) { - long extra = allDay ? ALL_DAY_MASK : 0; - - switch (response) { - case Attendees.ATTENDEE_STATUS_NONE: - extra |= ATTENDEE_STATUS_NONE_MASK; - break; - case Attendees.ATTENDEE_STATUS_ACCEPTED: - extra |= ATTENDEE_STATUS_ACCEPTED_MASK; - break; - case Attendees.ATTENDEE_STATUS_DECLINED: - extra |= ATTENDEE_STATUS_DECLINED_MASK; - break; - case Attendees.ATTENDEE_STATUS_TENTATIVE: - extra |= ATTENDEE_STATUS_TENTATIVE_MASK; - break; - default: - Log.wtf(TAG,"Unknown attendee response " + response); - extra |= ATTENDEE_STATUS_NONE_MASK; - break; - } - return extra; - } - } - - /** - * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time - * can be ignored - */ - public static final long EXTRA_GOTO_DATE = 1; - public static final long EXTRA_GOTO_TIME = 2; - public static final long EXTRA_GOTO_BACK_TO_PREVIOUS = 4; - public static final long EXTRA_GOTO_TODAY = 8; - - public interface EventHandler { - long getSupportedEventTypes(); - void handleEvent(EventInfo event); - - /** - * This notifies the handler that the database has changed and it should - * update its view. - */ - void eventsChanged(); - } - - /** - * Creates and/or returns an instance of CalendarController associated with - * the supplied context. It is best to pass in the current Activity. - * - * @param context The activity if at all possible. - */ - public static CalendarController getInstance(Context context) { - synchronized (instances) { - CalendarController controller = null; - WeakReference weakController = instances.get(context); - if (weakController != null) { - controller = weakController.get(); - } - - if (controller == null) { - controller = new CalendarController(context); - instances.put(context, new WeakReference(controller)); - } - return controller; - } - } - - /** - * Removes an instance when it is no longer needed. This should be called in - * an activity's onDestroy method. - * - * @param context The activity used to create the controller - */ - public static void removeInstance(Context context) { - instances.remove(context); - } - - private CalendarController(Context context) { - mContext = context; - mUpdateTimezone.run(); - mTime.setToNow(); - mDetailViewType = Utils.getSharedPreference(mContext, - GeneralPreferences.KEY_DETAILED_VIEW, - GeneralPreferences.DEFAULT_DETAILED_VIEW); - } - - public void sendEventRelatedEvent(Object sender, long eventType, long eventId, long startMillis, - long endMillis, int x, int y, long selectedMillis) { - // TODO: pass the real allDay status or at least a status that says we don't know the - // status and have the receiver query the data. - // The current use of this method for VIEW_EVENT is by the day view to show an EventInfo - // so currently the missing allDay status has no effect. - sendEventRelatedEventWithExtra(sender, eventType, eventId, startMillis, endMillis, x, y, - EventInfo.buildViewExtraLong(Attendees.ATTENDEE_STATUS_NONE, false), - selectedMillis); - } - - /** - * Helper for sending New/View/Edit/Delete events - * - * @param sender object of the caller - * @param eventType one of {@link EventType} - * @param eventId event id - * @param startMillis start time - * @param endMillis end time - * @param x x coordinate in the activity space - * @param y y coordinate in the activity space - * @param extraLong default response value for the "simple event view" and all day indication. - * Use Attendees.ATTENDEE_STATUS_NONE for no response. - * @param selectedMillis The time to specify as selected - */ - public void sendEventRelatedEventWithExtra(Object sender, long eventType, long eventId, - long startMillis, long endMillis, int x, int y, long extraLong, long selectedMillis) { - sendEventRelatedEventWithExtraWithTitleWithCalendarId(sender, eventType, eventId, - startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1); - } - - /** - * Helper for sending New/View/Edit/Delete events - * - * @param sender object of the caller - * @param eventType one of {@link EventType} - * @param eventId event id - * @param startMillis start time - * @param endMillis end time - * @param x x coordinate in the activity space - * @param y y coordinate in the activity space - * @param extraLong default response value for the "simple event view" and all day indication. - * Use Attendees.ATTENDEE_STATUS_NONE for no response. - * @param selectedMillis The time to specify as selected - * @param title The title of the event - * @param calendarId The id of the calendar which the event belongs to - */ - public void sendEventRelatedEventWithExtraWithTitleWithCalendarId(Object sender, long eventType, - long eventId, long startMillis, long endMillis, int x, int y, long extraLong, - long selectedMillis, String title, long calendarId) { - EventInfo info = new EventInfo(); - info.eventType = eventType; - if (eventType == EventType.VIEW_EVENT_DETAILS) { - info.viewType = ViewType.CURRENT; - } - - info.id = eventId; - info.startTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); - info.startTime.set(startMillis); - if (selectedMillis != -1) { - info.selectedTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); - info.selectedTime.set(selectedMillis); - } else { - info.selectedTime = info.startTime; - } - info.endTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); - info.endTime.set(endMillis); - info.x = x; - info.y = y; - info.extraLong = extraLong; - info.eventTitle = title; - info.calendarId = calendarId; - this.sendEvent(sender, info); - } - /** - * Helper for sending non-calendar-event events - * - * @param sender object of the caller - * @param eventType one of {@link EventType} - * @param start start time - * @param end end time - * @param eventId event id - * @param viewType {@link ViewType} - */ - public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId, - int viewType) { - sendEvent(sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null, - null); - } - - /** - * sendEvent() variant with extraLong, search query, and search component name. - */ - public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId, - int viewType, long extraLong, String query, ComponentName componentName) { - sendEvent(sender, eventType, start, end, start, eventId, viewType, extraLong, query, - componentName); - } - - public void sendEvent(Object sender, long eventType, Time start, Time end, Time selected, - long eventId, int viewType, long extraLong, String query, ComponentName componentName) { - EventInfo info = new EventInfo(); - info.eventType = eventType; - info.startTime = start; - info.selectedTime = selected; - info.endTime = end; - info.id = eventId; - info.viewType = viewType; - info.query = query; - info.componentName = componentName; - info.extraLong = extraLong; - this.sendEvent(sender, info); - } - - public void sendEvent(Object sender, final EventInfo event) { - // TODO Throw exception on invalid events - - if (DEBUG) { - Log.d(TAG, eventInfoToString(event)); - } - - Long filteredTypes = filters.get(sender); - if (filteredTypes != null && (filteredTypes.longValue() & event.eventType) != 0) { - // Suppress event per filter - if (DEBUG) { - Log.d(TAG, "Event suppressed"); - } - return; - } - - mPreviousViewType = mViewType; - - // Fix up view if not specified - if (event.viewType == ViewType.DETAIL) { - event.viewType = mDetailViewType; - mViewType = mDetailViewType; - } else if (event.viewType == ViewType.CURRENT) { - event.viewType = mViewType; - } else if (event.viewType != ViewType.EDIT) { - mViewType = event.viewType; - - if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY - || (Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK)) { - mDetailViewType = mViewType; - } - } - - if (DEBUG) { - Log.d(TAG, "vvvvvvvvvvvvvvv"); - Log.d(TAG, "Start " + (event.startTime == null ? "null" : event.startTime.toString())); - Log.d(TAG, "End " + (event.endTime == null ? "null" : event.endTime.toString())); - Log.d(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString())); - Log.d(TAG, "mTime " + (mTime == null ? "null" : mTime.toString())); - } - - long startMillis = 0; - if (event.startTime != null) { - startMillis = event.startTime.toMillis(false); - } - - // Set mTime if selectedTime is set - if (event.selectedTime != null && event.selectedTime.toMillis(false) != 0) { - mTime.set(event.selectedTime); - } else { - if (startMillis != 0) { - // selectedTime is not set so set mTime to startTime iff it is not - // within start and end times - long mtimeMillis = mTime.toMillis(false); - if (mtimeMillis < startMillis - || (event.endTime != null && mtimeMillis > event.endTime.toMillis(false))) { - mTime.set(event.startTime); - } - } - event.selectedTime = mTime; - } - // Store the formatting flags if this is an update to the title - if (event.eventType == EventType.UPDATE_TITLE) { - mDateFlags = event.extraLong; - } - - // Fix up start time if not specified - if (startMillis == 0) { - event.startTime = mTime; - } - if (DEBUG) { - Log.d(TAG, "Start " + (event.startTime == null ? "null" : event.startTime.toString())); - Log.d(TAG, "End " + (event.endTime == null ? "null" : event.endTime.toString())); - Log.d(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString())); - Log.d(TAG, "mTime " + (mTime == null ? "null" : mTime.toString())); - Log.d(TAG, "^^^^^^^^^^^^^^^"); - } - - // Store the eventId if we're entering edit event - if ((event.eventType - & (EventType.VIEW_EVENT_DETAILS)) - != 0) { - if (event.id > 0) { - mEventId = event.id; - } else { - mEventId = -1; - } - } - - boolean handled = false; - synchronized (this) { - mDispatchInProgressCounter ++; - - if (DEBUG) { - Log.d(TAG, "sendEvent: Dispatching to " + eventHandlers.size() + " handlers"); - } - // Dispatch to event handler(s) - if (mFirstEventHandler != null) { - // Handle the 'first' one before handling the others - EventHandler handler = mFirstEventHandler.second; - if (handler != null && (handler.getSupportedEventTypes() & event.eventType) != 0 - && !mToBeRemovedEventHandlers.contains(mFirstEventHandler.first)) { - handler.handleEvent(event); - handled = true; - } - } - for (Iterator> handlers = - eventHandlers.entrySet().iterator(); handlers.hasNext();) { - Entry entry = handlers.next(); - int key = entry.getKey(); - if (mFirstEventHandler != null && key == mFirstEventHandler.first) { - // If this was the 'first' handler it was already handled - continue; - } - EventHandler eventHandler = entry.getValue(); - if (eventHandler != null - && (eventHandler.getSupportedEventTypes() & event.eventType) != 0) { - if (mToBeRemovedEventHandlers.contains(key)) { - continue; - } - eventHandler.handleEvent(event); - handled = true; - } - } - - mDispatchInProgressCounter --; - - if (mDispatchInProgressCounter == 0) { - - // Deregister removed handlers - if (mToBeRemovedEventHandlers.size() > 0) { - for (Integer zombie : mToBeRemovedEventHandlers) { - eventHandlers.remove(zombie); - if (mFirstEventHandler != null && zombie.equals(mFirstEventHandler.first)) { - mFirstEventHandler = null; - } - } - mToBeRemovedEventHandlers.clear(); - } - // Add new handlers - if (mToBeAddedFirstEventHandler != null) { - mFirstEventHandler = mToBeAddedFirstEventHandler; - mToBeAddedFirstEventHandler = null; - } - if (mToBeAddedEventHandlers.size() > 0) { - for (Entry food : mToBeAddedEventHandlers.entrySet()) { - eventHandlers.put(food.getKey(), food.getValue()); - } - } - } - } - } - - /** - * Adds or updates an event handler. This uses a LinkedHashMap so that we can - * replace fragments based on the view id they are being expanded into. - * - * @param key The view id or placeholder for this handler - * @param eventHandler Typically a fragment or activity in the calendar app - */ - public void registerEventHandler(int key, EventHandler eventHandler) { - synchronized (this) { - if (mDispatchInProgressCounter > 0) { - mToBeAddedEventHandlers.put(key, eventHandler); - } else { - eventHandlers.put(key, eventHandler); - } - } - } - - public void registerFirstEventHandler(int key, EventHandler eventHandler) { - synchronized (this) { - registerEventHandler(key, eventHandler); - if (mDispatchInProgressCounter > 0) { - mToBeAddedFirstEventHandler = new Pair(key, eventHandler); - } else { - mFirstEventHandler = new Pair(key, eventHandler); - } - } - } - - public void deregisterEventHandler(Integer key) { - synchronized (this) { - if (mDispatchInProgressCounter > 0) { - // To avoid ConcurrencyException, stash away the event handler for now. - mToBeRemovedEventHandlers.add(key); - } else { - eventHandlers.remove(key); - if (mFirstEventHandler != null && mFirstEventHandler.first == key) { - mFirstEventHandler = null; - } - } - } - } - - public void deregisterAllEventHandlers() { - synchronized (this) { - if (mDispatchInProgressCounter > 0) { - // To avoid ConcurrencyException, stash away the event handler for now. - mToBeRemovedEventHandlers.addAll(eventHandlers.keySet()); - } else { - eventHandlers.clear(); - mFirstEventHandler = null; - } - } - } - - // FRAG_TODO doesn't work yet - public void filterBroadcasts(Object sender, long eventTypes) { - filters.put(sender, eventTypes); - } - - /** - * @return the time that this controller is currently pointed at - */ - public long getTime() { - return mTime.toMillis(false); - } - - /** - * @return the last set of date flags sent with - * {@link EventType#UPDATE_TITLE} - */ - public long getDateFlags() { - return mDateFlags; - } - - /** - * Set the time this controller is currently pointed at - * - * @param millisTime Time since epoch in millis - */ - public void setTime(long millisTime) { - mTime.set(millisTime); - } - - /** - * @return the last event ID the edit view was launched with - */ - public long getEventId() { - return mEventId; - } - - public int getViewType() { - return mViewType; - } - - public int getPreviousViewType() { - return mPreviousViewType; - } - - public void launchViewEvent(long eventId, long startMillis, long endMillis, int response) { - Intent intent = new Intent(Intent.ACTION_VIEW); - Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId); - intent.setData(eventUri); - intent.setClass(mContext, AllInOneActivity.class); - intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis); - intent.putExtra(EXTRA_EVENT_END_TIME, endMillis); - intent.putExtra(ATTENDEE_STATUS, response); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivity(intent); - } - - // Forces the viewType. Should only be used for initialization. - public void setViewType(int viewType) { - mViewType = viewType; - } - - // Sets the eventId. Should only be used for initialization. - public void setEventId(long eventId) { - mEventId = eventId; - } - - private String eventInfoToString(EventInfo eventInfo) { - String tmp = "Unknown"; - - StringBuilder builder = new StringBuilder(); - if ((eventInfo.eventType & EventType.GO_TO) != 0) { - tmp = "Go to time/event"; - } else if ((eventInfo.eventType & EventType.VIEW_EVENT) != 0) { - tmp = "View event"; - } else if ((eventInfo.eventType & EventType.VIEW_EVENT_DETAILS) != 0) { - tmp = "View details"; - } else if ((eventInfo.eventType & EventType.EVENTS_CHANGED) != 0) { - tmp = "Refresh events"; - } else if ((eventInfo.eventType & EventType.USER_HOME) != 0) { - tmp = "Gone home"; - } else if ((eventInfo.eventType & EventType.UPDATE_TITLE) != 0) { - tmp = "Update title"; - } - builder.append(tmp); - builder.append(": id="); - builder.append(eventInfo.id); - builder.append(", selected="); - builder.append(eventInfo.selectedTime); - builder.append(", start="); - builder.append(eventInfo.startTime); - builder.append(", end="); - builder.append(eventInfo.endTime); - builder.append(", viewType="); - builder.append(eventInfo.viewType); - builder.append(", x="); - builder.append(eventInfo.x); - builder.append(", y="); - builder.append(eventInfo.y); - return builder.toString(); - } -} diff --git a/src/com/android/calendar/CalendarData.java b/src/com/android/calendar/CalendarData.java deleted file mode 100644 index 5c8456fa..00000000 --- a/src/com/android/calendar/CalendarData.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2006 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; - -public final class CalendarData { - static final String[] s12HoursNoAmPm = { "12", "1", "2", "3", "4", - "5", "6", "7", "8", "9", "10", "11", "12", - "1", "2", "3", "4", "5", "6", "7", "8", - "9", "10", "11", "12" }; - - static final String[] s24Hours = { "00", "01", "02", "03", "04", "05", - "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", - "17", "18", "19", "20", "21", "22", "23", "00" }; -} diff --git a/src/com/android/calendar/CalendarUtils.java b/src/com/android/calendar/CalendarUtils.java deleted file mode 100644 index 0238c321..00000000 --- a/src/com/android/calendar/CalendarUtils.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (C) 2010 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.ContentValues; -import android.content.Context; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.os.Looper; -import android.provider.CalendarContract.CalendarCache; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; - -import java.util.Formatter; -import java.util.HashSet; -import java.util.Locale; - -/** - * A class containing utility methods related to Calendar apps. - * - * This class is expected to move into the app framework eventually. - */ -public class CalendarUtils { - private static final boolean DEBUG = false; - private static final String TAG = "CalendarUtils"; - - /** - * This class contains methods specific to reading and writing time zone - * values. - */ - public static class TimeZoneUtils { - private static final String[] TIMEZONE_TYPE_ARGS = { CalendarCache.KEY_TIMEZONE_TYPE }; - private static final String[] TIMEZONE_INSTANCES_ARGS = - { CalendarCache.KEY_TIMEZONE_INSTANCES }; - public static final String[] CALENDAR_CACHE_POJECTION = { - CalendarCache.KEY, CalendarCache.VALUE - }; - - private static StringBuilder mSB = new StringBuilder(50); - private static Formatter mF = new Formatter(mSB, Locale.getDefault()); - private volatile static boolean mFirstTZRequest = true; - private volatile static boolean mTZQueryInProgress = false; - - private volatile static boolean mUseHomeTZ = false; - private volatile static String mHomeTZ = Time.getCurrentTimezone(); - - private static HashSet mTZCallbacks = new HashSet(); - private static int mToken = 1; - private static AsyncTZHandler mHandler; - - // The name of the shared preferences file. This name must be maintained for historical - // reasons, as it's what PreferenceManager assigned the first time the file was created. - private final String mPrefsName; - - /** - * This is the key used for writing whether or not a home time zone should - * be used in the Calendar app to the Calendar Preferences. - */ - public static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled"; - /** - * This is the key used for writing the time zone that should be used if - * home time zones are enabled for the Calendar app. - */ - public static final String KEY_HOME_TZ = "preferences_home_tz"; - - /** - * This is a helper class for handling the async queries and updates for the - * time zone settings in Calendar. - */ - private class AsyncTZHandler extends AsyncQueryHandler { - public AsyncTZHandler(ContentResolver cr) { - super(cr); - } - - @Override - protected void onQueryComplete(int token, Object cookie, Cursor cursor) { - synchronized (mTZCallbacks) { - if (cursor == null) { - mTZQueryInProgress = false; - mFirstTZRequest = true; - return; - } - - boolean writePrefs = false; - // Check the values in the db - int keyColumn = cursor.getColumnIndexOrThrow(CalendarCache.KEY); - int valueColumn = cursor.getColumnIndexOrThrow(CalendarCache.VALUE); - while(cursor.moveToNext()) { - String key = cursor.getString(keyColumn); - String value = cursor.getString(valueColumn); - if (TextUtils.equals(key, CalendarCache.KEY_TIMEZONE_TYPE)) { - boolean useHomeTZ = !TextUtils.equals( - value, CalendarCache.TIMEZONE_TYPE_AUTO); - if (useHomeTZ != mUseHomeTZ) { - writePrefs = true; - mUseHomeTZ = useHomeTZ; - } - } else if (TextUtils.equals( - key, CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) { - if (!TextUtils.isEmpty(value) && !TextUtils.equals(mHomeTZ, value)) { - writePrefs = true; - mHomeTZ = value; - } - } - } - cursor.close(); - if (writePrefs) { - SharedPreferences prefs = getSharedPreferences((Context)cookie, mPrefsName); - // Write the prefs - setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ); - setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ); - } - - mTZQueryInProgress = false; - for (Runnable callback : mTZCallbacks) { - if (callback != null) { - callback.run(); - } - } - mTZCallbacks.clear(); - } - } - } - - /** - * The name of the file where the shared prefs for Calendar are stored - * must be provided. All activities within an app should provide the - * same preferences name or behavior may become erratic. - * - * @param prefsName - */ - public TimeZoneUtils(String prefsName) { - mPrefsName = prefsName; - } - - /** - * Formats a date or a time range according to the local conventions. - * - * This formats a date/time range using Calendar's time zone and the - * local conventions for the region of the device. - * - * If the {@link DateUtils#FORMAT_UTC} flag is used it will pass in - * the UTC time zone instead. - * - * @param context the context is required only if the time is shown - * @param startMillis the start time in UTC milliseconds - * @param endMillis the end time in UTC milliseconds - * @param flags a bit mask of options See - * {@link DateUtils#formatDateRange(Context, Formatter, long, long, int, String) formatDateRange} - * @return a string containing the formatted date/time range. - */ - public String formatDateRange(Context context, long startMillis, - long endMillis, int flags) { - String date; - String tz; - if ((flags & DateUtils.FORMAT_UTC) != 0) { - tz = Time.TIMEZONE_UTC; - } else { - tz = getTimeZone(context, null); - } - synchronized (mSB) { - mSB.setLength(0); - date = DateUtils.formatDateRange(context, mF, startMillis, endMillis, flags, - tz).toString(); - } - return date; - } - - /** - * Writes a new home time zone to the db. - * - * Updates the home time zone in the db asynchronously and updates - * the local cache. Sending a time zone of - * {@link CalendarCache#TIMEZONE_TYPE_AUTO} will cause it to be set - * to the device's time zone. null or empty tz will be ignored. - * - * @param context The calling activity - * @param timeZone The time zone to set Calendar to, or - * {@link CalendarCache#TIMEZONE_TYPE_AUTO} - */ - public void setTimeZone(Context context, String timeZone) { - if (TextUtils.isEmpty(timeZone)) { - if (DEBUG) { - Log.d(TAG, "Empty time zone, nothing to be done."); - } - return; - } - boolean updatePrefs = false; - synchronized (mTZCallbacks) { - if (CalendarCache.TIMEZONE_TYPE_AUTO.equals(timeZone)) { - if (mUseHomeTZ) { - updatePrefs = true; - } - mUseHomeTZ = false; - } else { - if (!mUseHomeTZ || !TextUtils.equals(mHomeTZ, timeZone)) { - updatePrefs = true; - } - mUseHomeTZ = true; - mHomeTZ = timeZone; - } - } - if (updatePrefs) { - // Write the prefs - SharedPreferences prefs = getSharedPreferences(context, mPrefsName); - setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ); - setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ); - - // Update the db - ContentValues values = new ContentValues(); - if (mHandler != null) { - mHandler.cancelOperation(mToken); - } - - mHandler = new AsyncTZHandler(context.getContentResolver()); - - // skip 0 so query can use it - if (++mToken == 0) { - mToken = 1; - } - - // Write the use home tz setting - values.put(CalendarCache.VALUE, mUseHomeTZ ? CalendarCache.TIMEZONE_TYPE_HOME - : CalendarCache.TIMEZONE_TYPE_AUTO); - mHandler.startUpdate(mToken, null, CalendarCache.URI, values, "key=?", - TIMEZONE_TYPE_ARGS); - - // If using a home tz write it to the db - if (mUseHomeTZ) { - ContentValues values2 = new ContentValues(); - values2.put(CalendarCache.VALUE, mHomeTZ); - mHandler.startUpdate(mToken, null, CalendarCache.URI, values2, - "key=?", TIMEZONE_INSTANCES_ARGS); - } - } - } - - /** - * Gets the time zone that Calendar should be displayed in - * - * This is a helper method to get the appropriate time zone for Calendar. If this - * is the first time this method has been called it will initiate an asynchronous - * query to verify that the data in preferences is correct. The callback supplied - * will only be called if this query returns a value other than what is stored in - * preferences and should cause the calling activity to refresh anything that - * depends on calling this method. - * - * @param context The calling activity - * @param callback The runnable that should execute if a query returns new values - * @return The string value representing the time zone Calendar should display - */ - public String getTimeZone(Context context, Runnable callback) { - synchronized (mTZCallbacks){ - if (mFirstTZRequest) { - SharedPreferences prefs = getSharedPreferences(context, mPrefsName); - mUseHomeTZ = prefs.getBoolean(KEY_HOME_TZ_ENABLED, false); - mHomeTZ = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()); - - // Only check content resolver if we have a looper to attach to use - if (Looper.myLooper() != null) { - mTZQueryInProgress = true; - mFirstTZRequest = false; - - // When the async query returns it should synchronize on - // mTZCallbacks, update mUseHomeTZ, mHomeTZ, and the - // preferences, set mTZQueryInProgress to false, and call all - // the runnables in mTZCallbacks. - if (mHandler == null) { - mHandler = new AsyncTZHandler(context.getContentResolver()); - } - mHandler.startQuery(0, context, CalendarCache.URI, CALENDAR_CACHE_POJECTION, - null, null, null); - } - } - if (mTZQueryInProgress) { - mTZCallbacks.add(callback); - } - } - return mUseHomeTZ ? mHomeTZ : Time.getCurrentTimezone(); - } - - /** - * Forces a query of the database to check for changes to the time zone. - * This should be called if another app may have modified the db. If a - * query is already in progress the callback will be added to the list - * of callbacks to be called when it returns. - * - * @param context The calling activity - * @param callback The runnable that should execute if a query returns - * new values - */ - public void forceDBRequery(Context context, Runnable callback) { - synchronized (mTZCallbacks){ - if (mTZQueryInProgress) { - mTZCallbacks.add(callback); - return; - } - mFirstTZRequest = true; - getTimeZone(context, callback); - } - } - } - - /** - * A helper method for writing a String value to the preferences - * asynchronously. - * - * @param context A context with access to the correct preferences - * @param key The preference to write to - * @param value The value to write - */ - public static void setSharedPreference(SharedPreferences prefs, String key, String value) { -// SharedPreferences prefs = getSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - editor.putString(key, value); - editor.apply(); - } - - /** - * A helper method for writing a boolean value to the preferences - * asynchronously. - * - * @param context A context with access to the correct preferences - * @param key The preference to write to - * @param value The value to write - */ - public static void setSharedPreference(SharedPreferences prefs, String key, boolean value) { -// SharedPreferences prefs = getSharedPreferences(context, prefsName); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(key, value); - editor.apply(); - } - - /** Return a properly configured SharedPreferences instance */ - public static SharedPreferences getSharedPreferences(Context context, String prefsName) { - return context.getSharedPreferences(prefsName, Context.MODE_PRIVATE); - } -} diff --git a/src/com/android/calendar/CalendarViewAdapter.java b/src/com/android/calendar/CalendarViewAdapter.java deleted file mode 100644 index 524268fc..00000000 --- a/src/com/android/calendar/CalendarViewAdapter.java +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.calendar.CalendarController.ViewType; - -import android.content.Context; -import android.os.Handler; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.TextView; - -import java.util.Formatter; -import java.util.Locale; - - -/* - * The MenuSpinnerAdapter defines the look of the ActionBar's pull down menu - * for small screen layouts. The pull down menu replaces the tabs uses for big screen layouts - * - * The MenuSpinnerAdapter responsible for creating the views used for in the pull down menu. - */ - -public class CalendarViewAdapter extends BaseAdapter { - - private static final String TAG = "MenuSpinnerAdapter"; - - private final String mButtonNames []; // Text on buttons - - // Used to define the look of the menu button according to the current view: - // Day view: show day of the week + full date underneath - // Week view: show the month + year - // Month view: show the month + year - // Agenda view: show day of the week + full date underneath - private int mCurrentMainView; - - private final LayoutInflater mInflater; - - // Defines the types of view returned by this spinner - private static final int BUTTON_VIEW_TYPE = 0; - static final int VIEW_TYPE_NUM = 1; // Increase this if you add more view types - - public static final int DAY_BUTTON_INDEX = 0; - public static final int WEEK_BUTTON_INDEX = 1; - public static final int MONTH_BUTTON_INDEX = 2; - public static final int AGENDA_BUTTON_INDEX = 3; - - // The current selected event's time, used to calculate the date and day of the week - // for the buttons. - private long mMilliTime; - private String mTimeZone; - private long mTodayJulianDay; - - private final Context mContext; - private final Formatter mFormatter; - private final StringBuilder mStringBuilder; - private Handler mMidnightHandler = null; // Used to run a time update every midnight - private final boolean mShowDate; // Spinner mode indicator (view name or view name with date) - - // Updates time specific variables (time-zone, today's Julian day). - private final Runnable mTimeUpdater = new Runnable() { - @Override - public void run() { - refresh(mContext); - } - }; - - public CalendarViewAdapter(Context context, int viewType, boolean showDate) { - super(); - - mMidnightHandler = new Handler(); - mCurrentMainView = viewType; - mContext = context; - mShowDate = showDate; - - // Initialize - mButtonNames = context.getResources().getStringArray(R.array.buttons_list); - mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mStringBuilder = new StringBuilder(50); - mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); - - // Sets time specific variables and starts a thread for midnight updates - if (showDate) { - refresh(context); - } - } - - - // Sets the time zone and today's Julian day to be used by the adapter. - // Also, notify listener on the change and resets the midnight update thread. - public void refresh(Context context) { - mTimeZone = Utils.getTimeZone(context, mTimeUpdater); - Time time = new Time(mTimeZone); - long now = System.currentTimeMillis(); - time.set(now); - mTodayJulianDay = Time.getJulianDay(now, time.gmtoff); - notifyDataSetChanged(); - setMidnightHandler(); - } - - // Sets a thread to run 1 second after midnight and update the current date - // This is used to display correctly the date of yesterday/today/tomorrow - private void setMidnightHandler() { - mMidnightHandler.removeCallbacks(mTimeUpdater); - // Set the time updater to run at 1 second after midnight - long now = System.currentTimeMillis(); - Time time = new Time(mTimeZone); - time.set(now); - long runInMillis = (24 * 3600 - time.hour * 3600 - time.minute * 60 - - time.second + 1) * 1000; - mMidnightHandler.postDelayed(mTimeUpdater, runInMillis); - } - - // Stops the midnight update thread, called by the activity when it is paused. - public void onPause() { - mMidnightHandler.removeCallbacks(mTimeUpdater); - } - - // Returns the amount of buttons in the menu - @Override - public int getCount() { - return mButtonNames.length; - } - - - @Override - public Object getItem(int position) { - if (position < mButtonNames.length) { - return mButtonNames[position]; - } - return null; - } - - @Override - public long getItemId(int position) { - // Item ID is its location in the list - return position; - } - - @Override - public boolean hasStableIds() { - return false; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - - View v; - - if (mShowDate) { - // Check if can recycle the view - if (convertView == null || ((Integer) convertView.getTag()).intValue() - != R.layout.actionbar_pulldown_menu_top_button) { - v = mInflater.inflate(R.layout.actionbar_pulldown_menu_top_button, parent, false); - // Set the tag to make sure you can recycle it when you get it - // as a convert view - v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button)); - } else { - v = convertView; - } - TextView weekDay = (TextView) v.findViewById(R.id.top_button_weekday); - TextView date = (TextView) v.findViewById(R.id.top_button_date); - - switch (mCurrentMainView) { - case ViewType.DAY: - weekDay.setVisibility(View.VISIBLE); - weekDay.setText(buildDayOfWeek()); - date.setText(buildFullDate()); - break; - case ViewType.WEEK: - if (Utils.getShowWeekNumber(mContext)) { - weekDay.setVisibility(View.VISIBLE); - weekDay.setText(buildWeekNum()); - } else { - weekDay.setVisibility(View.GONE); - } - date.setText(buildMonthYearDate()); - break; - case ViewType.MONTH: - weekDay.setVisibility(View.GONE); - date.setText(buildMonthYearDate()); - break; - default: - v = null; - break; - } - } else { - if (convertView == null || ((Integer) convertView.getTag()).intValue() - != R.layout.actionbar_pulldown_menu_top_button_no_date) { - v = mInflater.inflate( - R.layout.actionbar_pulldown_menu_top_button_no_date, parent, false); - // Set the tag to make sure you can recycle it when you get it - // as a convert view - v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button_no_date)); - } else { - v = convertView; - } - TextView title = (TextView) v; - switch (mCurrentMainView) { - case ViewType.DAY: - title.setText(mButtonNames [DAY_BUTTON_INDEX]); - break; - case ViewType.WEEK: - title.setText(mButtonNames [WEEK_BUTTON_INDEX]); - break; - case ViewType.MONTH: - title.setText(mButtonNames [MONTH_BUTTON_INDEX]); - break; - default: - v = null; - break; - } - } - return v; - } - - @Override - public int getItemViewType(int position) { - // Only one kind of view is used - return BUTTON_VIEW_TYPE; - } - - @Override - public int getViewTypeCount() { - return VIEW_TYPE_NUM; - } - - @Override - public boolean isEmpty() { - return (mButtonNames.length == 0); - } - - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - View v = mInflater.inflate(R.layout.actionbar_pulldown_menu_button, parent, false); - TextView viewType = (TextView)v.findViewById(R.id.button_view); - TextView date = (TextView)v.findViewById(R.id.button_date); - switch (position) { - case DAY_BUTTON_INDEX: - viewType.setText(mButtonNames [DAY_BUTTON_INDEX]); - if (mShowDate) { - date.setText(buildMonthDayDate()); - } - break; - case WEEK_BUTTON_INDEX: - viewType.setText(mButtonNames [WEEK_BUTTON_INDEX]); - if (mShowDate) { - date.setText(buildWeekDate()); - } - break; - case MONTH_BUTTON_INDEX: - viewType.setText(mButtonNames [MONTH_BUTTON_INDEX]); - if (mShowDate) { - date.setText(buildMonthDate()); - } - break; - default: - v = convertView; - break; - } - return v; - } - - // Updates the current viewType - // Used to match the label on the menu button with the calendar view - public void setMainView(int viewType) { - mCurrentMainView = viewType; - notifyDataSetChanged(); - } - - // Update the date that is displayed on buttons - // Used when the user selects a new day/week/month to watch - public void setTime(long time) { - mMilliTime = time; - notifyDataSetChanged(); - } - - // Builds a string with the day of the week and the word yesterday/today/tomorrow - // before it if applicable. - private String buildDayOfWeek() { - - Time t = new Time(mTimeZone); - t.set(mMilliTime); - long julianDay = Time.getJulianDay(mMilliTime,t.gmtoff); - String dayOfWeek = null; - mStringBuilder.setLength(0); - - if (julianDay == mTodayJulianDay) { - dayOfWeek = mContext.getString(R.string.agenda_today, - DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()); - } else if (julianDay == mTodayJulianDay - 1) { - dayOfWeek = mContext.getString(R.string.agenda_yesterday, - DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()); - } else if (julianDay == mTodayJulianDay + 1) { - dayOfWeek = mContext.getString(R.string.agenda_tomorrow, - DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()); - } else { - dayOfWeek = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString(); - } - return dayOfWeek.toUpperCase(); - } - - // Builds strings with different formats: - // Full date: Month,day Year - // Month year - // Month day - // Month - // Week: month day-day or month day - month day - private String buildFullDate() { - mStringBuilder.setLength(0); - String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString(); - return date; - } - - private String buildMonthYearDate() { - mStringBuilder.setLength(0); - String date = DateUtils.formatDateRange( - mContext, - mFormatter, - mMilliTime, - mMilliTime, - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY - | DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString(); - return date; - } - - private String buildMonthDayDate() { - mStringBuilder.setLength(0); - String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime, - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR, mTimeZone).toString(); - return date; - } - - private String buildMonthDate() { - mStringBuilder.setLength(0); - String date = DateUtils.formatDateRange( - mContext, - mFormatter, - mMilliTime, - mMilliTime, - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR - | DateUtils.FORMAT_NO_MONTH_DAY, mTimeZone).toString(); - return date; - } - - private String buildWeekDate() { - // Calculate the start of the week, taking into account the "first day of the week" - // setting. - - Time t = new Time(mTimeZone); - t.set(mMilliTime); - int firstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - int dayOfWeek = t.weekDay; - int diff = dayOfWeek - firstDayOfWeek; - if (diff != 0) { - if (diff < 0) { - diff += 7; - } - t.monthDay -= diff; - t.normalize(true /* ignore isDst */); - } - - long weekStartTime = t.toMillis(true); - // The end of the week is 6 days after the start of the week - long weekEndTime = weekStartTime + DateUtils.WEEK_IN_MILLIS - DateUtils.DAY_IN_MILLIS; - - // If week start and end is in 2 different months, use short months names - Time t1 = new Time(mTimeZone); - t.set(weekEndTime); - int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR; - if (t.month != t1.month) { - flags |= DateUtils.FORMAT_ABBREV_MONTH; - } - - mStringBuilder.setLength(0); - String date = DateUtils.formatDateRange(mContext, mFormatter, weekStartTime, - weekEndTime, flags, mTimeZone).toString(); - return date; - } - - private String buildWeekNum() { - int week = Utils.getWeekNumberFromTime(mMilliTime, mContext); - return mContext.getResources().getQuantityString(R.plurals.weekN, week, week); - } - -} diff --git a/src/com/android/calendar/DayFragment.java b/src/com/android/calendar/DayFragment.java deleted file mode 100644 index a9fb39ed..00000000 --- a/src/com/android/calendar/DayFragment.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2010 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 com.android.calendar.CalendarController.EventInfo; -import com.android.calendar.CalendarController.EventType; - -import android.app.Fragment; -import android.content.Context; -import android.os.Bundle; -import android.text.format.Time; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.ProgressBar; -import android.widget.ViewSwitcher; -import android.widget.ViewSwitcher.ViewFactory; - -/** - * This is the base class for Day and Week Activities. - */ -public class DayFragment extends Fragment implements CalendarController.EventHandler, ViewFactory { - /** - * The view id used for all the views we create. It's OK to have all child - * views have the same ID. This ID is used to pick which view receives - * focus when a view hierarchy is saved / restore - */ - private static final int VIEW_ID = 1; - - protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time"; - - protected ProgressBar mProgressBar; - protected ViewSwitcher mViewSwitcher; - protected Animation mInAnimationForward; - protected Animation mOutAnimationForward; - protected Animation mInAnimationBackward; - protected Animation mOutAnimationBackward; - EventLoader mEventLoader; - - Time mSelectedDay = new Time(); - - private final Runnable mTZUpdater = new Runnable() { - @Override - public void run() { - if (!DayFragment.this.isAdded()) { - return; - } - String tz = Utils.getTimeZone(getActivity(), mTZUpdater); - mSelectedDay.timezone = tz; - mSelectedDay.normalize(true); - } - }; - - private int mNumDays; - - public DayFragment() { - mSelectedDay.setToNow(); - } - - public DayFragment(long timeMillis, int numOfDays) { - mNumDays = numOfDays; - if (timeMillis == 0) { - mSelectedDay.setToNow(); - } else { - mSelectedDay.set(timeMillis); - } - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - Context context = getActivity(); - - mInAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_in); - mOutAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_out); - mInAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_in); - mOutAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_out); - - mEventLoader = new EventLoader(context); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.day_activity, null); - - mViewSwitcher = (ViewSwitcher) v.findViewById(R.id.switcher); - mViewSwitcher.setFactory(this); - mViewSwitcher.getCurrentView().requestFocus(); - ((DayView) mViewSwitcher.getCurrentView()).updateTitle(); - - return v; - } - - public View makeView() { - mTZUpdater.run(); - DayView view = new DayView(getActivity(), CalendarController - .getInstance(getActivity()), mViewSwitcher, mEventLoader, mNumDays); - view.setId(VIEW_ID); - view.setLayoutParams(new ViewSwitcher.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - view.setSelected(mSelectedDay, false, false); - return view; - } - - @Override - public void onResume() { - super.onResume(); - mEventLoader.startBackgroundThread(); - mTZUpdater.run(); - eventsChanged(); - DayView view = (DayView) mViewSwitcher.getCurrentView(); - view.handleOnResume(); - view.restartCurrentTimeUpdates(); - - view = (DayView) mViewSwitcher.getNextView(); - view.handleOnResume(); - view.restartCurrentTimeUpdates(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - } - - @Override - public void onPause() { - super.onPause(); - DayView view = (DayView) mViewSwitcher.getCurrentView(); - view.cleanup(); - view = (DayView) mViewSwitcher.getNextView(); - view.cleanup(); - mEventLoader.stopBackgroundThread(); - - // Stop events cross-fade animation - view.stopEventsAnimation(); - ((DayView) mViewSwitcher.getNextView()).stopEventsAnimation(); - } - - void startProgressSpinner() { - // start the progress spinner - mProgressBar.setVisibility(View.VISIBLE); - } - - void stopProgressSpinner() { - // stop the progress spinner - mProgressBar.setVisibility(View.GONE); - } - - private void goTo(Time goToTime, boolean ignoreTime, boolean animateToday) { - if (mViewSwitcher == null) { - // The view hasn't been set yet. Just save the time and use it later. - mSelectedDay.set(goToTime); - return; - } - - DayView currentView = (DayView) mViewSwitcher.getCurrentView(); - - // How does goTo time compared to what's already displaying? - int diff = currentView.compareToVisibleTimeRange(goToTime); - - if (diff == 0) { - // In visible range. No need to switch view - currentView.setSelected(goToTime, ignoreTime, animateToday); - } else { - // Figure out which way to animate - if (diff > 0) { - mViewSwitcher.setInAnimation(mInAnimationForward); - mViewSwitcher.setOutAnimation(mOutAnimationForward); - } else { - mViewSwitcher.setInAnimation(mInAnimationBackward); - mViewSwitcher.setOutAnimation(mOutAnimationBackward); - } - - DayView next = (DayView) mViewSwitcher.getNextView(); - if (ignoreTime) { - next.setFirstVisibleHour(currentView.getFirstVisibleHour()); - } - - next.setSelected(goToTime, ignoreTime, animateToday); - next.reloadEvents(); - mViewSwitcher.showNext(); - next.requestFocus(); - next.updateTitle(); - next.restartCurrentTimeUpdates(); - } - } - - /** - * Returns the selected time in milliseconds. The milliseconds are measured - * in UTC milliseconds from the epoch and uniquely specifies any selectable - * time. - * - * @return the selected time in milliseconds - */ - public long getSelectedTimeInMillis() { - if (mViewSwitcher == null) { - return -1; - } - DayView view = (DayView) mViewSwitcher.getCurrentView(); - if (view == null) { - return -1; - } - return view.getSelectedTimeInMillis(); - } - - public void eventsChanged() { - if (mViewSwitcher == null) { - return; - } - DayView view = (DayView) mViewSwitcher.getCurrentView(); - view.clearCachedEvents(); - view.reloadEvents(); - - view = (DayView) mViewSwitcher.getNextView(); - view.clearCachedEvents(); - } - - public DayView getNextView() { - return (DayView) mViewSwitcher.getNextView(); - } - - public long getSupportedEventTypes() { - return EventType.GO_TO | EventType.EVENTS_CHANGED; - } - - public void handleEvent(EventInfo msg) { - if (msg.eventType == EventType.GO_TO) { -// TODO support a range of time -// TODO support event_id -// TODO support select message - goTo(msg.selectedTime, (msg.extraLong & CalendarController.EXTRA_GOTO_DATE) != 0, - (msg.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0); - } else if (msg.eventType == EventType.EVENTS_CHANGED) { - eventsChanged(); - } - } -} diff --git a/src/com/android/calendar/DayOfMonthDrawable.java b/src/com/android/calendar/DayOfMonthDrawable.java deleted file mode 100644 index 461ab317..00000000 --- a/src/com/android/calendar/DayOfMonthDrawable.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2012 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.Context; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; - -/** - * A custom view to draw the day of the month in the today button in the options menu - */ - -public class DayOfMonthDrawable extends Drawable { - - private String mDayOfMonth = "1"; - private final Paint mPaint; - private final Rect mTextBounds = new Rect(); - private static float mTextSize = 14; - - public DayOfMonthDrawable(Context c) { - mTextSize = c.getResources().getDimension(R.dimen.today_icon_text_size); - mPaint = new Paint(); - mPaint.setAlpha(255); - mPaint.setColor(0xFF777777); - mPaint.setTypeface(Typeface.DEFAULT_BOLD); - mPaint.setTextSize(mTextSize); - mPaint.setTextAlign(Paint.Align.CENTER); - } - - @Override - public void draw(Canvas canvas) { - mPaint.getTextBounds(mDayOfMonth, 0, mDayOfMonth.length(), mTextBounds); - int textHeight = mTextBounds.bottom - mTextBounds.top; - Rect bounds = getBounds(); - canvas.drawText(mDayOfMonth, bounds.right / 2, ((float) bounds.bottom + textHeight + 1) / 2, - mPaint); - } - - @Override - public void setAlpha(int alpha) { - mPaint.setAlpha(alpha); - } - - @Override - public void setColorFilter(ColorFilter cf) { - // Ignore - } - - @Override - public int getOpacity() { - return PixelFormat.UNKNOWN; - } - - public void setDayOfMonth(int day) { - mDayOfMonth = Integer.toString(day); - invalidateSelf(); - } -} diff --git a/src/com/android/calendar/DayView.java b/src/com/android/calendar/DayView.java deleted file mode 100644 index 2fc00b3c..00000000 --- a/src/com/android/calendar/DayView.java +++ /dev/null @@ -1,4008 +0,0 @@ -/* - * 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.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.app.AlertDialog; -import android.app.Service; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.Context; -import android.content.DialogInterface; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.database.Cursor; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Paint.Style; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Handler; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.text.Layout.Alignment; -import android.text.SpannableStringBuilder; -import android.text.StaticLayout; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.text.style.StyleSpan; -import android.util.Log; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.GestureDetector; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.Animation; -import android.view.animation.Interpolator; -import android.view.animation.TranslateAnimation; -import android.widget.EdgeEffect; -import android.widget.ImageView; -import android.widget.OverScroller; -import android.widget.PopupWindow; -import android.widget.TextView; -import android.widget.ViewSwitcher; - -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.CalendarController.ViewType; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Formatter; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * View for multi-day view. So far only 1 and 7 day have been tested. - */ -public class DayView extends View implements View.OnCreateContextMenuListener, - ScaleGestureDetector.OnScaleGestureListener, View.OnClickListener, View.OnLongClickListener - { - private static String TAG = "DayView"; - private static boolean DEBUG = false; - private static boolean DEBUG_SCALING = false; - private static final String PERIOD_SPACE = ". "; - - private static float mScale = 0; // Used for supporting different screen densities - private static final long INVALID_EVENT_ID = -1; //This is used for remembering a null event - // Duration of the allday expansion - private static final long ANIMATION_DURATION = 400; - // duration of the more allday event text fade - private static final long ANIMATION_SECONDARY_DURATION = 200; - // duration of the scroll to go to a specified time - private static final int GOTO_SCROLL_DURATION = 200; - // duration for events' cross-fade animation - private static final int EVENTS_CROSS_FADE_DURATION = 400; - // duration to show the event clicked - private static final int CLICK_DISPLAY_DURATION = 50; - - private static final int MENU_DAY = 3; - private static final int MENU_EVENT_VIEW = 5; - private static final int MENU_EVENT_CREATE = 6; - private static final int MENU_EVENT_EDIT = 7; - private static final int MENU_EVENT_DELETE = 8; - - private static int DEFAULT_CELL_HEIGHT = 64; - private static int MAX_CELL_HEIGHT = 150; - private static int MIN_Y_SPAN = 100; - - private boolean mOnFlingCalled; - private boolean mStartingScroll = false; - protected boolean mPaused = true; - private Handler mHandler; - /** - * ID of the last event which was displayed with the toast popup. - * - * This is used to prevent popping up multiple quick views for the same event, especially - * during calendar syncs. This becomes valid when an event is selected, either by default - * on starting calendar or by scrolling to an event. It becomes invalid when the user - * explicitly scrolls to an empty time slot, changes views, or deletes the event. - */ - private long mLastPopupEventID; - - protected Context mContext; - - private static final String[] CALENDARS_PROJECTION = new String[] { - Calendars._ID, // 0 - Calendars.CALENDAR_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 int FROM_NONE = 0; - private static final int FROM_ABOVE = 1; - private static final int FROM_BELOW = 2; - private static final int FROM_LEFT = 4; - private static final int FROM_RIGHT = 8; - - private static final int ACCESS_LEVEL_NONE = 0; - private static final int ACCESS_LEVEL_DELETE = 1; - private static final int ACCESS_LEVEL_EDIT = 2; - - private static int mHorizontalSnapBackThreshold = 128; - - private final ContinueScroll mContinueScroll = new ContinueScroll(); - - // Make this visible within the package for more informative debugging - Time mBaseDate; - private Time mCurrentTime; - //Update the current time line every five minutes if the window is left open that long - private static final int UPDATE_CURRENT_TIME_DELAY = 300000; - private final UpdateCurrentTime mUpdateCurrentTime = new UpdateCurrentTime(); - private int mTodayJulianDay; - - private final Typeface mBold = Typeface.DEFAULT_BOLD; - private int mFirstJulianDay; - private int mLoadedFirstJulianDay = -1; - private int mLastJulianDay; - - private int mMonthLength; - private int mFirstVisibleDate; - private int mFirstVisibleDayOfWeek; - private int[] mEarliestStartHour; // indexed by the week day offset - private boolean[] mHasAllDayEvent; // indexed by the week day offset - private String mEventCountTemplate; - private Event mClickedEvent; // The event the user clicked on - private Event mSavedClickedEvent; - private static int mOnDownDelay; - private int mClickedYLocation; - private long mDownTouchTime; - - private int mEventsAlpha = 255; - private ObjectAnimator mEventsCrossFadeAnimation; - - protected static StringBuilder mStringBuilder = new StringBuilder(50); - // TODO recreate formatter when locale changes - protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); - - private final Runnable mTZUpdater = new Runnable() { - @Override - public void run() { - String tz = Utils.getTimeZone(mContext, this); - mBaseDate.timezone = tz; - mBaseDate.normalize(true); - mCurrentTime.switchTimezone(tz); - invalidate(); - } - }; - - // Sets the "clicked" color from the clicked event - private final Runnable mSetClick = new Runnable() { - @Override - public void run() { - mClickedEvent = mSavedClickedEvent; - mSavedClickedEvent = null; - DayView.this.invalidate(); - } - }; - - // Clears the "clicked" color from the clicked event and launch the event - private final Runnable mClearClick = new Runnable() { - @Override - public void run() { - if (mClickedEvent != null) { - mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, mClickedEvent.id, - mClickedEvent.startMillis, mClickedEvent.endMillis, - DayView.this.getWidth() / 2, mClickedYLocation, - getSelectedTimeInMillis()); - } - mClickedEvent = null; - DayView.this.invalidate(); - } - }; - - private final TodayAnimatorListener mTodayAnimatorListener = new TodayAnimatorListener(); - - class TodayAnimatorListener extends AnimatorListenerAdapter { - private volatile Animator mAnimator = null; - private volatile boolean mFadingIn = false; - - @Override - public void onAnimationEnd(Animator animation) { - synchronized (this) { - if (mAnimator != animation) { - animation.removeAllListeners(); - animation.cancel(); - return; - } - if (mFadingIn) { - if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners(); - mTodayAnimator.cancel(); - } - mTodayAnimator = ObjectAnimator - .ofInt(DayView.this, "animateTodayAlpha", 255, 0); - mAnimator = mTodayAnimator; - mFadingIn = false; - mTodayAnimator.addListener(this); - mTodayAnimator.setDuration(600); - mTodayAnimator.start(); - } else { - mAnimateToday = false; - mAnimateTodayAlpha = 0; - mAnimator.removeAllListeners(); - mAnimator = null; - mTodayAnimator = null; - invalidate(); - } - } - } - - public void setAnimator(Animator animation) { - mAnimator = animation; - } - - public void setFadingIn(boolean fadingIn) { - mFadingIn = fadingIn; - } - - } - - AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - mScrolling = true; - } - - @Override - public void onAnimationCancel(Animator animation) { - mScrolling = false; - } - - @Override - public void onAnimationEnd(Animator animation) { - mScrolling = false; - resetSelectedHour(); - invalidate(); - } - }; - - /** - * This variable helps to avoid unnecessarily reloading events by keeping - * track of the start millis parameter used for the most recent loading - * of events. If the next reload matches this, then the events are not - * reloaded. To force a reload, set this to zero (this is set to zero - * in the method clearCachedEvents()). - */ - private long mLastReloadMillis; - - private ArrayList mEvents = new ArrayList(); - private ArrayList mAllDayEvents = new ArrayList(); - private StaticLayout[] mLayouts = null; - private StaticLayout[] mAllDayLayouts = null; - private int mSelectionDay; // Julian day - private int mSelectionHour; - - boolean mSelectionAllday; - - // Current selection info for accessibility - private int mSelectionDayForAccessibility; // Julian day - private int mSelectionHourForAccessibility; - private Event mSelectedEventForAccessibility; - // Last selection info for accessibility - private int mLastSelectionDayForAccessibility; - private int mLastSelectionHourForAccessibility; - private Event mLastSelectedEventForAccessibility; - - - /** Width of a day or non-conflicting event */ - private int mCellWidth; - - // Pre-allocate these objects and re-use them - private final Rect mRect = new Rect(); - private final Rect mDestRect = new Rect(); - private final Rect mSelectionRect = new Rect(); - // This encloses the more allDay events icon - private final Rect mExpandAllDayRect = new Rect(); - // TODO Clean up paint usage - private final Paint mPaint = new Paint(); - private final Paint mEventTextPaint = new Paint(); - private final Paint mSelectionPaint = new Paint(); - private float[] mLines; - - private int mFirstDayOfWeek; // First day of the week - - private PopupWindow mPopup; - private View mPopupView; - - // The number of milliseconds to show the popup window - private static final int POPUP_DISMISS_DELAY = 3000; - private final DismissPopup mDismissPopup = new DismissPopup(); - - private boolean mRemeasure = true; - - private final EventLoader mEventLoader; - protected final EventGeometry mEventGeometry; - - private static float GRID_LINE_LEFT_MARGIN = 0; - private static final float GRID_LINE_INNER_WIDTH = 1; - - private static final int DAY_GAP = 1; - private static final int HOUR_GAP = 1; - // This is the standard height of an allday event with no restrictions - private static int SINGLE_ALLDAY_HEIGHT = 34; - /** - * This is the minimum desired height of a allday event. - * When unexpanded, allday events will use this height. - * When expanded allDay events will attempt to grow to fit all - * events at this height. - */ - private static float MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = 28.0F; // in pixels - /** - * This is how big the unexpanded allday height is allowed to be. - * It will get adjusted based on screen size - */ - private static int MAX_UNEXPANDED_ALLDAY_HEIGHT = - (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4); - /** - * This is the minimum size reserved for displaying regular events. - * The expanded allDay region can't expand into this. - */ - private static int MIN_HOURS_HEIGHT = 180; - private static int ALLDAY_TOP_MARGIN = 1; - // The largest a single allDay event will become. - private static int MAX_HEIGHT_OF_ONE_ALLDAY_EVENT = 34; - - private static int HOURS_TOP_MARGIN = 2; - private static int HOURS_LEFT_MARGIN = 2; - private static int HOURS_RIGHT_MARGIN = 4; - private static int HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN; - private static int NEW_EVENT_MARGIN = 4; - private static int NEW_EVENT_WIDTH = 2; - private static int NEW_EVENT_MAX_LENGTH = 16; - - private static int CURRENT_TIME_LINE_SIDE_BUFFER = 4; - private static int CURRENT_TIME_LINE_TOP_OFFSET = 2; - - /* package */ static final int MINUTES_PER_HOUR = 60; - /* package */ static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * 24; - /* package */ static final int MILLIS_PER_MINUTE = 60 * 1000; - /* package */ static final int MILLIS_PER_HOUR = (3600 * 1000); - /* package */ static final int MILLIS_PER_DAY = MILLIS_PER_HOUR * 24; - - // More events text will transition between invisible and this alpha - private static final int MORE_EVENTS_MAX_ALPHA = 0x4C; - private static int DAY_HEADER_ONE_DAY_LEFT_MARGIN = 0; - private static int DAY_HEADER_ONE_DAY_RIGHT_MARGIN = 5; - private static int DAY_HEADER_ONE_DAY_BOTTOM_MARGIN = 6; - private static int DAY_HEADER_RIGHT_MARGIN = 4; - private static int DAY_HEADER_BOTTOM_MARGIN = 3; - private static float DAY_HEADER_FONT_SIZE = 14; - private static float DATE_HEADER_FONT_SIZE = 32; - private static float NORMAL_FONT_SIZE = 12; - private static float EVENT_TEXT_FONT_SIZE = 12; - private static float HOURS_TEXT_SIZE = 12; - private static float AMPM_TEXT_SIZE = 9; - private static int MIN_HOURS_WIDTH = 96; - private static int MIN_CELL_WIDTH_FOR_TEXT = 20; - private static final int MAX_EVENT_TEXT_LEN = 500; - // smallest height to draw an event with - private static float MIN_EVENT_HEIGHT = 24.0F; // in pixels - private static int CALENDAR_COLOR_SQUARE_SIZE = 10; - private static int EVENT_RECT_TOP_MARGIN = 1; - private static int EVENT_RECT_BOTTOM_MARGIN = 0; - private static int EVENT_RECT_LEFT_MARGIN = 1; - private static int EVENT_RECT_RIGHT_MARGIN = 0; - private static int EVENT_RECT_STROKE_WIDTH = 2; - private static int EVENT_TEXT_TOP_MARGIN = 2; - private static int EVENT_TEXT_BOTTOM_MARGIN = 2; - private static int EVENT_TEXT_LEFT_MARGIN = 6; - private static int EVENT_TEXT_RIGHT_MARGIN = 6; - private static int ALL_DAY_EVENT_RECT_BOTTOM_MARGIN = 1; - private static int EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN; - private static int EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_BOTTOM_MARGIN; - private static int EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN; - private static int EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_RIGHT_MARGIN; - // margins and sizing for the expand allday icon - private static int EXPAND_ALL_DAY_BOTTOM_MARGIN = 10; - // sizing for "box +n" in allDay events - private static int EVENT_SQUARE_WIDTH = 10; - private static int EVENT_LINE_PADDING = 4; - private static int NEW_EVENT_HINT_FONT_SIZE = 12; - - private static int mEventTextColor; - private static int mMoreEventsTextColor; - - private static int mWeek_saturdayColor; - private static int mWeek_sundayColor; - private static int mCalendarDateBannerTextColor; - private static int mCalendarAmPmLabel; - private static int mCalendarGridAreaSelected; - private static int mCalendarGridLineInnerHorizontalColor; - private static int mCalendarGridLineInnerVerticalColor; - private static int mFutureBgColor; - private static int mFutureBgColorRes; - private static int mBgColor; - private static int mNewEventHintColor; - private static int mCalendarHourLabelColor; - private static int mMoreAlldayEventsTextAlpha = MORE_EVENTS_MAX_ALPHA; - - private float mAnimationDistance = 0; - private int mViewStartX; - private int mViewStartY; - private int mMaxViewStartY; - private int mViewHeight; - private int mViewWidth; - private int mGridAreaHeight = -1; - private static int mCellHeight = 0; // shared among all DayViews - private static int mMinCellHeight = 32; - private int mScrollStartY; - private int mPreviousDirection; - private static int mScaledPagingTouchSlop = 0; - - /** - * Vertical distance or span between the two touch points at the start of a - * scaling gesture - */ - private float mStartingSpanY = 0; - /** Height of 1 hour in pixels at the start of a scaling gesture */ - private int mCellHeightBeforeScaleGesture; - /** The hour at the center two touch points */ - private float mGestureCenterHour = 0; - - private boolean mRecalCenterHour = false; - - /** - * Flag to decide whether to handle the up event. Cases where up events - * should be ignored are 1) right after a scale gesture and 2) finger was - * down before app launch - */ - private boolean mHandleActionUp = true; - - private int mHoursTextHeight; - /** - * The height of the area used for allday events - */ - private int mAlldayHeight; - /** - * The height of the allday event area used during animation - */ - private int mAnimateDayHeight = 0; - /** - * The height of an individual allday event during animation - */ - private int mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; - /** - * Whether to use the expand or collapse icon. - */ - private static boolean mUseExpandIcon = true; - /** - * The height of the day names/numbers - */ - private static int DAY_HEADER_HEIGHT = 45; - /** - * The height of the day names/numbers for multi-day views - */ - private static int MULTI_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT; - /** - * The height of the day names/numbers when viewing a single day - */ - private static int ONE_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT; - /** - * Max of all day events in a given day in this view. - */ - private int mMaxAlldayEvents; - /** - * A count of the number of allday events that were not drawn for each day - */ - private int[] mSkippedAlldayEvents; - /** - * The number of allDay events at which point we start hiding allDay events. - */ - private int mMaxUnexpandedAlldayEventCount = 4; - /** - * Whether or not to expand the allDay area to fill the screen - */ - private static boolean mShowAllAllDayEvents = false; - - protected int mNumDays = 7; - private int mNumHours = 10; - - /** Width of the time line (list of hours) to the left. */ - private int mHoursWidth; - private int mDateStrWidth; - /** Top of the scrollable region i.e. below date labels and all day events */ - private int mFirstCell; - /** First fully visibile hour */ - private int mFirstHour = -1; - /** Distance between the mFirstCell and the top of first fully visible hour. */ - private int mFirstHourOffset; - private String[] mHourStrs; - private String[] mDayStrs; - private String[] mDayStrs2Letter; - private boolean mIs24HourFormat; - - private final ArrayList mSelectedEvents = new ArrayList(); - private boolean mComputeSelectedEvents; - private boolean mUpdateToast; - private Event mSelectedEvent; - private Event mPrevSelectedEvent; - private final Rect mPrevBox = new Rect(); - protected final Resources mResources; - protected final Drawable mCurrentTimeLine; - protected final Drawable mCurrentTimeAnimateLine; - protected final Drawable mTodayHeaderDrawable; - protected final Drawable mExpandAlldayDrawable; - protected final Drawable mCollapseAlldayDrawable; - protected Drawable mAcceptedOrTentativeEventBoxDrawable; - private String mAmString; - private String mPmString; - private static int sCounter = 0; - - ScaleGestureDetector mScaleGestureDetector; - - /** - * The initial state of the touch mode when we enter this view. - */ - private static final int TOUCH_MODE_INITIAL_STATE = 0; - - /** - * Indicates we just received the touch event and we are waiting to see if - * it is a tap or a scroll gesture. - */ - private static final int TOUCH_MODE_DOWN = 1; - - /** - * Indicates the touch gesture is a vertical scroll - */ - private static final int TOUCH_MODE_VSCROLL = 0x20; - - /** - * Indicates the touch gesture is a horizontal scroll - */ - private static final int TOUCH_MODE_HSCROLL = 0x40; - - private int mTouchMode = TOUCH_MODE_INITIAL_STATE; - - /** - * The selection modes are HIDDEN, PRESSED, SELECTED, and LONGPRESS. - */ - private static final int SELECTION_HIDDEN = 0; - private static final int SELECTION_PRESSED = 1; // D-pad down but not up yet - private static final int SELECTION_SELECTED = 2; - private static final int SELECTION_LONGPRESS = 3; - - private int mSelectionMode = SELECTION_HIDDEN; - - private boolean mScrolling = false; - - // Pixels scrolled - private float mInitialScrollX; - private float mInitialScrollY; - - private boolean mAnimateToday = false; - private int mAnimateTodayAlpha = 0; - - // Animates the height of the allday region - ObjectAnimator mAlldayAnimator; - // Animates the height of events in the allday region - ObjectAnimator mAlldayEventAnimator; - // Animates the transparency of the more events text - ObjectAnimator mMoreAlldayEventsAnimator; - // Animates the current time marker when Today is pressed - ObjectAnimator mTodayAnimator; - // whether or not an event is stopping because it was cancelled - private boolean mCancellingAnimations = false; - // tracks whether a touch originated in the allday area - private boolean mTouchStartedInAlldayArea = false; - - private final CalendarController mController; - private final ViewSwitcher mViewSwitcher; - private final GestureDetector mGestureDetector; - private final OverScroller mScroller; - private final EdgeEffect mEdgeEffectTop; - private final EdgeEffect mEdgeEffectBottom; - private boolean mCallEdgeEffectOnAbsorb; - private final int OVERFLING_DISTANCE; - private float mLastVelocity; - - private final ScrollInterpolator mHScrollInterpolator; - private AccessibilityManager mAccessibilityMgr = null; - private boolean mIsAccessibilityEnabled = false; - private boolean mTouchExplorationEnabled = false; - private final String mNewEventHintString; - - public DayView(Context context, CalendarController controller, - ViewSwitcher viewSwitcher, EventLoader eventLoader, int numDays) { - super(context); - mContext = context; - initAccessibilityVariables(); - - mResources = context.getResources(); - mNewEventHintString = mResources.getString(R.string.day_view_new_event_hint); - mNumDays = numDays; - - DATE_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.date_header_text_size); - DAY_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.day_label_text_size); - ONE_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.one_day_header_height); - DAY_HEADER_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.day_header_bottom_margin); - EXPAND_ALL_DAY_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.all_day_bottom_margin); - HOURS_TEXT_SIZE = (int) mResources.getDimension(R.dimen.hours_text_size); - AMPM_TEXT_SIZE = (int) mResources.getDimension(R.dimen.ampm_text_size); - MIN_HOURS_WIDTH = (int) mResources.getDimension(R.dimen.min_hours_width); - HOURS_LEFT_MARGIN = (int) mResources.getDimension(R.dimen.hours_left_margin); - HOURS_RIGHT_MARGIN = (int) mResources.getDimension(R.dimen.hours_right_margin); - MULTI_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.day_header_height); - int eventTextSizeId; - if (mNumDays == 1) { - eventTextSizeId = R.dimen.day_view_event_text_size; - } else { - eventTextSizeId = R.dimen.week_view_event_text_size; - } - EVENT_TEXT_FONT_SIZE = (int) mResources.getDimension(eventTextSizeId); - NEW_EVENT_HINT_FONT_SIZE = (int) mResources.getDimension(R.dimen.new_event_hint_text_size); - MIN_EVENT_HEIGHT = mResources.getDimension(R.dimen.event_min_height); - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = MIN_EVENT_HEIGHT; - EVENT_TEXT_TOP_MARGIN = (int) mResources.getDimension(R.dimen.event_text_vertical_margin); - EVENT_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN; - EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN; - EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN; - - EVENT_TEXT_LEFT_MARGIN = (int) mResources - .getDimension(R.dimen.event_text_horizontal_margin); - EVENT_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN; - EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN; - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN; - - if (mScale == 0) { - - mScale = mResources.getDisplayMetrics().density; - if (mScale != 1) { - SINGLE_ALLDAY_HEIGHT *= mScale; - ALLDAY_TOP_MARGIN *= mScale; - MAX_HEIGHT_OF_ONE_ALLDAY_EVENT *= mScale; - - NORMAL_FONT_SIZE *= mScale; - GRID_LINE_LEFT_MARGIN *= mScale; - HOURS_TOP_MARGIN *= mScale; - MIN_CELL_WIDTH_FOR_TEXT *= mScale; - MAX_UNEXPANDED_ALLDAY_HEIGHT *= mScale; - mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; - - CURRENT_TIME_LINE_SIDE_BUFFER *= mScale; - CURRENT_TIME_LINE_TOP_OFFSET *= mScale; - - MIN_Y_SPAN *= mScale; - MAX_CELL_HEIGHT *= mScale; - DEFAULT_CELL_HEIGHT *= mScale; - DAY_HEADER_HEIGHT *= mScale; - DAY_HEADER_RIGHT_MARGIN *= mScale; - DAY_HEADER_ONE_DAY_LEFT_MARGIN *= mScale; - DAY_HEADER_ONE_DAY_RIGHT_MARGIN *= mScale; - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN *= mScale; - CALENDAR_COLOR_SQUARE_SIZE *= mScale; - EVENT_RECT_TOP_MARGIN *= mScale; - EVENT_RECT_BOTTOM_MARGIN *= mScale; - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN *= mScale; - EVENT_RECT_LEFT_MARGIN *= mScale; - EVENT_RECT_RIGHT_MARGIN *= mScale; - EVENT_RECT_STROKE_WIDTH *= mScale; - EVENT_SQUARE_WIDTH *= mScale; - EVENT_LINE_PADDING *= mScale; - NEW_EVENT_MARGIN *= mScale; - NEW_EVENT_WIDTH *= mScale; - NEW_EVENT_MAX_LENGTH *= mScale; - } - } - HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN; - DAY_HEADER_HEIGHT = mNumDays == 1 ? ONE_DAY_HEADER_HEIGHT : MULTI_DAY_HEADER_HEIGHT; - - mCurrentTimeLine = mResources.getDrawable(R.drawable.timeline_indicator_holo_light); - mCurrentTimeAnimateLine = mResources - .getDrawable(R.drawable.timeline_indicator_activated_holo_light); - mTodayHeaderDrawable = mResources.getDrawable(R.drawable.today_blue_week_holo_light); - mExpandAlldayDrawable = mResources.getDrawable(R.drawable.ic_expand_holo_light); - mCollapseAlldayDrawable = mResources.getDrawable(R.drawable.ic_collapse_holo_light); - mNewEventHintColor = mResources.getColor(R.color.new_event_hint_text_color); - mAcceptedOrTentativeEventBoxDrawable = mResources - .getDrawable(R.drawable.panel_month_event_holo_light); - - mEventLoader = eventLoader; - mEventGeometry = new EventGeometry(); - mEventGeometry.setMinEventHeight(MIN_EVENT_HEIGHT); - mEventGeometry.setHourGap(HOUR_GAP); - mEventGeometry.setCellMargin(DAY_GAP); - mLastPopupEventID = INVALID_EVENT_ID; - mController = controller; - mViewSwitcher = viewSwitcher; - mGestureDetector = new GestureDetector(context, new CalendarGestureListener()); - mScaleGestureDetector = new ScaleGestureDetector(getContext(), this); - if (mCellHeight == 0) { - mCellHeight = Utils.getSharedPreference(mContext, - GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, DEFAULT_CELL_HEIGHT); - } - mScroller = new OverScroller(context); - mHScrollInterpolator = new ScrollInterpolator(); - mEdgeEffectTop = new EdgeEffect(context); - mEdgeEffectBottom = new EdgeEffect(context); - ViewConfiguration vc = ViewConfiguration.get(context); - mScaledPagingTouchSlop = vc.getScaledPagingTouchSlop(); - mOnDownDelay = ViewConfiguration.getTapTimeout(); - OVERFLING_DISTANCE = vc.getScaledOverflingDistance(); - - init(context); - } - - @Override - protected void onAttachedToWindow() { - if (mHandler == null) { - mHandler = getHandler(); - mHandler.post(mUpdateCurrentTime); - } - } - - private void init(Context context) { - setFocusable(true); - - // Allow focus in touch mode so that we can do keyboard shortcuts - // even after we've entered touch mode. - setFocusableInTouchMode(true); - setClickable(true); - setOnCreateContextMenuListener(this); - - mFirstDayOfWeek = Utils.getFirstDayOfWeek(context); - - mCurrentTime = new Time(Utils.getTimeZone(context, mTZUpdater)); - long currentTime = System.currentTimeMillis(); - mCurrentTime.set(currentTime); - mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff); - - mWeek_saturdayColor = mResources.getColor(R.color.week_saturday); - mWeek_sundayColor = mResources.getColor(R.color.week_sunday); - mCalendarDateBannerTextColor = mResources.getColor(R.color.calendar_date_banner_text_color); - mFutureBgColorRes = mResources.getColor(R.color.calendar_future_bg_color); - mBgColor = mResources.getColor(R.color.calendar_hour_background); - mCalendarAmPmLabel = mResources.getColor(R.color.calendar_ampm_label); - mCalendarGridAreaSelected = mResources.getColor(R.color.calendar_grid_area_selected); - mCalendarGridLineInnerHorizontalColor = mResources - .getColor(R.color.calendar_grid_line_inner_horizontal_color); - mCalendarGridLineInnerVerticalColor = mResources - .getColor(R.color.calendar_grid_line_inner_vertical_color); - mCalendarHourLabelColor = mResources.getColor(R.color.calendar_hour_label); - mEventTextColor = mResources.getColor(R.color.calendar_event_text_color); - mMoreEventsTextColor = mResources.getColor(R.color.month_event_other_color); - - mEventTextPaint.setTextSize(EVENT_TEXT_FONT_SIZE); - mEventTextPaint.setTextAlign(Paint.Align.LEFT); - mEventTextPaint.setAntiAlias(true); - - int gridLineColor = mResources.getColor(R.color.calendar_grid_line_highlight_color); - Paint p = mSelectionPaint; - p.setColor(gridLineColor); - p.setStyle(Style.FILL); - p.setAntiAlias(false); - - p = mPaint; - p.setAntiAlias(true); - - // Allocate space for 2 weeks worth of weekday names so that we can - // easily start the week display at any week day. - mDayStrs = new String[14]; - - // Also create an array of 2-letter abbreviations. - mDayStrs2Letter = new String[14]; - - for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { - int index = i - Calendar.SUNDAY; - // e.g. Tue for Tuesday - mDayStrs[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_MEDIUM) - .toUpperCase(); - mDayStrs[index + 7] = mDayStrs[index]; - // e.g. Tu for Tuesday - mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORT) - .toUpperCase(); - - // If we don't have 2-letter day strings, fall back to 1-letter. - if (mDayStrs2Letter[index].equals(mDayStrs[index])) { - mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORTEST); - } - - mDayStrs2Letter[index + 7] = mDayStrs2Letter[index]; - } - - // Figure out how much space we need for the 3-letter abbrev names - // in the worst case. - p.setTextSize(DATE_HEADER_FONT_SIZE); - p.setTypeface(mBold); - String[] dateStrs = {" 28", " 30"}; - mDateStrWidth = computeMaxStringWidth(0, dateStrs, p); - p.setTextSize(DAY_HEADER_FONT_SIZE); - mDateStrWidth += computeMaxStringWidth(0, mDayStrs, p); - - p.setTextSize(HOURS_TEXT_SIZE); - p.setTypeface(null); - handleOnResume(); - - mAmString = DateUtils.getAMPMString(Calendar.AM).toUpperCase(); - mPmString = DateUtils.getAMPMString(Calendar.PM).toUpperCase(); - String[] ampm = {mAmString, mPmString}; - p.setTextSize(AMPM_TEXT_SIZE); - mHoursWidth = Math.max(HOURS_MARGIN, computeMaxStringWidth(mHoursWidth, ampm, p) - + HOURS_RIGHT_MARGIN); - mHoursWidth = Math.max(MIN_HOURS_WIDTH, mHoursWidth); - - LayoutInflater inflater; - inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mPopupView = inflater.inflate(R.layout.bubble_event, null); - mPopupView.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - mPopup = new PopupWindow(context); - mPopup.setContentView(mPopupView); - Resources.Theme dialogTheme = getResources().newTheme(); - dialogTheme.applyStyle(android.R.style.Theme_Dialog, true); - TypedArray ta = dialogTheme.obtainStyledAttributes(new int[] { - android.R.attr.windowBackground }); - mPopup.setBackgroundDrawable(ta.getDrawable(0)); - ta.recycle(); - - // Enable touching the popup window - mPopupView.setOnClickListener(this); - // Catch long clicks for creating a new event - setOnLongClickListener(this); - - mBaseDate = new Time(Utils.getTimeZone(context, mTZUpdater)); - long millis = System.currentTimeMillis(); - mBaseDate.set(millis); - - mEarliestStartHour = new int[mNumDays]; - mHasAllDayEvent = new boolean[mNumDays]; - - // mLines is the array of points used with Canvas.drawLines() in - // drawGridBackground() and drawAllDayEvents(). Its size depends - // on the max number of lines that can ever be drawn by any single - // drawLines() call in either of those methods. - final int maxGridLines = (24 + 1) // max horizontal lines we might draw - + (mNumDays + 1); // max vertical lines we might draw - mLines = new float[maxGridLines * 4]; - } - - /** - * This is called when the popup window is pressed. - */ - public void onClick(View v) { - if (v == mPopupView) { - // Pretend it was a trackball click because that will always - // jump to the "View event" screen. - switchViews(true /* trackball */); - } - } - - public void handleOnResume() { - initAccessibilityVariables(); - if(Utils.getSharedPreference(mContext, OtherPreferences.KEY_OTHER_1, false)) { - mFutureBgColor = 0; - } else { - mFutureBgColor = mFutureBgColorRes; - } - mIs24HourFormat = DateFormat.is24HourFormat(mContext); - mHourStrs = mIs24HourFormat ? CalendarData.s24Hours : CalendarData.s12HoursNoAmPm; - mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - mLastSelectionDayForAccessibility = 0; - mLastSelectionHourForAccessibility = 0; - mLastSelectedEventForAccessibility = null; - mSelectionMode = SELECTION_HIDDEN; - } - - private void initAccessibilityVariables() { - mAccessibilityMgr = (AccessibilityManager) mContext - .getSystemService(Service.ACCESSIBILITY_SERVICE); - mIsAccessibilityEnabled = mAccessibilityMgr != null && mAccessibilityMgr.isEnabled(); - mTouchExplorationEnabled = isTouchExplorationEnabled(); - } - - /** - * Returns the start of the selected time in milliseconds since the epoch. - * - * @return selected time in UTC milliseconds since the epoch. - */ - long getSelectedTimeInMillis() { - Time time = new Time(mBaseDate); - time.setJulianDay(mSelectionDay); - time.hour = mSelectionHour; - - // We ignore the "isDst" field because we want normalize() to figure - // out the correct DST value and not adjust the selected time based - // on the current setting of DST. - return time.normalize(true /* ignore isDst */); - } - - Time getSelectedTime() { - Time time = new Time(mBaseDate); - time.setJulianDay(mSelectionDay); - time.hour = mSelectionHour; - - // We ignore the "isDst" field because we want normalize() to figure - // out the correct DST value and not adjust the selected time based - // on the current setting of DST. - time.normalize(true /* ignore isDst */); - return time; - } - - Time getSelectedTimeForAccessibility() { - Time time = new Time(mBaseDate); - time.setJulianDay(mSelectionDayForAccessibility); - time.hour = mSelectionHourForAccessibility; - - // We ignore the "isDst" field because we want normalize() to figure - // out the correct DST value and not adjust the selected time based - // on the current setting of DST. - time.normalize(true /* ignore isDst */); - return time; - } - - /** - * Returns the start of the selected time in minutes since midnight, - * local time. The derived class must ensure that this is consistent - * with the return value from getSelectedTimeInMillis(). - */ - int getSelectedMinutesSinceMidnight() { - return mSelectionHour * MINUTES_PER_HOUR; - } - - int getFirstVisibleHour() { - return mFirstHour; - } - - void setFirstVisibleHour(int firstHour) { - mFirstHour = firstHour; - mFirstHourOffset = 0; - } - - public void setSelected(Time time, boolean ignoreTime, boolean animateToday) { - mBaseDate.set(time); - setSelectedHour(mBaseDate.hour); - setSelectedEvent(null); - mPrevSelectedEvent = null; - long millis = mBaseDate.toMillis(false /* use isDst */); - setSelectedDay(Time.getJulianDay(millis, mBaseDate.gmtoff)); - mSelectedEvents.clear(); - mComputeSelectedEvents = true; - - int gotoY = Integer.MIN_VALUE; - - if (!ignoreTime && mGridAreaHeight != -1) { - int lastHour = 0; - - if (mBaseDate.hour < mFirstHour) { - // Above visible region - gotoY = mBaseDate.hour * (mCellHeight + HOUR_GAP); - } else { - lastHour = (mGridAreaHeight - mFirstHourOffset) / (mCellHeight + HOUR_GAP) - + mFirstHour; - - if (mBaseDate.hour >= lastHour) { - // Below visible region - - // target hour + 1 (to give it room to see the event) - - // grid height (to get the y of the top of the visible - // region) - gotoY = (int) ((mBaseDate.hour + 1 + mBaseDate.minute / 60.0f) - * (mCellHeight + HOUR_GAP) - mGridAreaHeight); - } - } - - if (DEBUG) { - Log.e(TAG, "Go " + gotoY + " 1st " + mFirstHour + ":" + mFirstHourOffset + "CH " - + (mCellHeight + HOUR_GAP) + " lh " + lastHour + " gh " + mGridAreaHeight - + " ymax " + mMaxViewStartY); - } - - if (gotoY > mMaxViewStartY) { - gotoY = mMaxViewStartY; - } else if (gotoY < 0 && gotoY != Integer.MIN_VALUE) { - gotoY = 0; - } - } - - recalc(); - - mRemeasure = true; - invalidate(); - - boolean delayAnimateToday = false; - if (gotoY != Integer.MIN_VALUE) { - ValueAnimator scrollAnim = ObjectAnimator.ofInt(this, "viewStartY", mViewStartY, gotoY); - scrollAnim.setDuration(GOTO_SCROLL_DURATION); - scrollAnim.setInterpolator(new AccelerateDecelerateInterpolator()); - scrollAnim.addListener(mAnimatorListener); - scrollAnim.start(); - delayAnimateToday = true; - } - if (animateToday) { - synchronized (mTodayAnimatorListener) { - if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners(); - mTodayAnimator.cancel(); - } - mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha", - mAnimateTodayAlpha, 255); - mAnimateToday = true; - mTodayAnimatorListener.setFadingIn(true); - mTodayAnimatorListener.setAnimator(mTodayAnimator); - mTodayAnimator.addListener(mTodayAnimatorListener); - mTodayAnimator.setDuration(150); - if (delayAnimateToday) { - mTodayAnimator.setStartDelay(GOTO_SCROLL_DURATION); - } - mTodayAnimator.start(); - } - } - sendAccessibilityEventAsNeeded(false); - } - - // Called from animation framework via reflection. Do not remove - public void setViewStartY(int viewStartY) { - if (viewStartY > mMaxViewStartY) { - viewStartY = mMaxViewStartY; - } - - mViewStartY = viewStartY; - - computeFirstHour(); - invalidate(); - } - - public void setAnimateTodayAlpha(int todayAlpha) { - mAnimateTodayAlpha = todayAlpha; - invalidate(); - } - - public Time getSelectedDay() { - Time time = new Time(mBaseDate); - time.setJulianDay(mSelectionDay); - time.hour = mSelectionHour; - - // We ignore the "isDst" field because we want normalize() to figure - // out the correct DST value and not adjust the selected time based - // on the current setting of DST. - time.normalize(true /* ignore isDst */); - return time; - } - - public void updateTitle() { - Time start = new Time(mBaseDate); - start.normalize(true); - Time end = new Time(start); - end.monthDay += mNumDays - 1; - // Move it forward one minute so the formatter doesn't lose a day - end.minute += 1; - end.normalize(true); - - long formatFlags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; - if (mNumDays != 1) { - // Don't show day of the month if for multi-day view - formatFlags |= DateUtils.FORMAT_NO_MONTH_DAY; - - // Abbreviate the month if showing multiple months - if (start.month != end.month) { - formatFlags |= DateUtils.FORMAT_ABBREV_MONTH; - } - } - - mController.sendEvent(this, EventType.UPDATE_TITLE, start, end, null, -1, ViewType.CURRENT, - formatFlags, null, null); - } - - /** - * return a negative number if "time" is comes before the visible time - * range, a positive number if "time" is after the visible time range, and 0 - * if it is in the visible time range. - */ - public int compareToVisibleTimeRange(Time time) { - - int savedHour = mBaseDate.hour; - int savedMinute = mBaseDate.minute; - int savedSec = mBaseDate.second; - - mBaseDate.hour = 0; - mBaseDate.minute = 0; - mBaseDate.second = 0; - - if (DEBUG) { - Log.d(TAG, "Begin " + mBaseDate.toString()); - Log.d(TAG, "Diff " + time.toString()); - } - - // Compare beginning of range - int diff = Time.compare(time, mBaseDate); - if (diff > 0) { - // Compare end of range - mBaseDate.monthDay += mNumDays; - mBaseDate.normalize(true); - diff = Time.compare(time, mBaseDate); - - if (DEBUG) Log.d(TAG, "End " + mBaseDate.toString()); - - mBaseDate.monthDay -= mNumDays; - mBaseDate.normalize(true); - if (diff < 0) { - // in visible time - diff = 0; - } else if (diff == 0) { - // Midnight of following day - diff = 1; - } - } - - if (DEBUG) Log.d(TAG, "Diff: " + diff); - - mBaseDate.hour = savedHour; - mBaseDate.minute = savedMinute; - mBaseDate.second = savedSec; - return diff; - } - - private void recalc() { - // Set the base date to the beginning of the week if we are displaying - // 7 days at a time. - if (mNumDays == 7) { - adjustToBeginningOfWeek(mBaseDate); - } - - final long start = mBaseDate.toMillis(false /* use isDst */); - mFirstJulianDay = Time.getJulianDay(start, mBaseDate.gmtoff); - mLastJulianDay = mFirstJulianDay + mNumDays - 1; - - mMonthLength = mBaseDate.getActualMaximum(Time.MONTH_DAY); - mFirstVisibleDate = mBaseDate.monthDay; - mFirstVisibleDayOfWeek = mBaseDate.weekDay; - } - - private void adjustToBeginningOfWeek(Time time) { - int dayOfWeek = time.weekDay; - int diff = dayOfWeek - mFirstDayOfWeek; - if (diff != 0) { - if (diff < 0) { - diff += 7; - } - time.monthDay -= diff; - time.normalize(true /* ignore isDst */); - } - } - - @Override - protected void onSizeChanged(int width, int height, int oldw, int oldh) { - mViewWidth = width; - mViewHeight = height; - mEdgeEffectTop.setSize(mViewWidth, mViewHeight); - mEdgeEffectBottom.setSize(mViewWidth, mViewHeight); - int gridAreaWidth = width - mHoursWidth; - mCellWidth = (gridAreaWidth - (mNumDays * DAY_GAP)) / mNumDays; - - // This would be about 1 day worth in a 7 day view - mHorizontalSnapBackThreshold = width / 7; - - Paint p = new Paint(); - p.setTextSize(HOURS_TEXT_SIZE); - mHoursTextHeight = (int) Math.abs(p.ascent()); - remeasure(width, height); - } - - /** - * Measures the space needed for various parts of the view after - * loading new events. This can change if there are all-day events. - */ - private void remeasure(int width, int height) { - // Shrink to fit available space but make sure we can display at least two events - MAX_UNEXPANDED_ALLDAY_HEIGHT = (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4); - MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.min(MAX_UNEXPANDED_ALLDAY_HEIGHT, height / 6); - MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.max(MAX_UNEXPANDED_ALLDAY_HEIGHT, - (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 2); - mMaxUnexpandedAlldayEventCount = - (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT / MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT); - - // First, clear the array of earliest start times, and the array - // indicating presence of an all-day event. - for (int day = 0; day < mNumDays; day++) { - mEarliestStartHour[day] = 25; // some big number - mHasAllDayEvent[day] = false; - } - - int maxAllDayEvents = mMaxAlldayEvents; - - // The min is where 24 hours cover the entire visible area - mMinCellHeight = Math.max((height - DAY_HEADER_HEIGHT) / 24, (int) MIN_EVENT_HEIGHT); - if (mCellHeight < mMinCellHeight) { - mCellHeight = mMinCellHeight; - } - - // Calculate mAllDayHeight - mFirstCell = DAY_HEADER_HEIGHT; - int allDayHeight = 0; - if (maxAllDayEvents > 0) { - int maxAllAllDayHeight = height - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; - // If there is at most one all-day event per day, then use less - // space (but more than the space for a single event). - if (maxAllDayEvents == 1) { - allDayHeight = SINGLE_ALLDAY_HEIGHT; - } else if (maxAllDayEvents <= mMaxUnexpandedAlldayEventCount){ - // Allow the all-day area to grow in height depending on the - // number of all-day events we need to show, up to a limit. - allDayHeight = maxAllDayEvents * MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; - if (allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) { - allDayHeight = MAX_UNEXPANDED_ALLDAY_HEIGHT; - } - } else { - // if we have more than the magic number, check if we're animating - // and if not adjust the sizes appropriately - if (mAnimateDayHeight != 0) { - // Don't shrink the space past the final allDay space. The animation - // continues to hide the last event so the more events text can - // fade in. - allDayHeight = Math.max(mAnimateDayHeight, MAX_UNEXPANDED_ALLDAY_HEIGHT); - } else { - // Try to fit all the events in - allDayHeight = (int) (maxAllDayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT); - // But clip the area depending on which mode we're in - if (!mShowAllAllDayEvents && allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) { - allDayHeight = (int) (mMaxUnexpandedAlldayEventCount * - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT); - } else if (allDayHeight > maxAllAllDayHeight) { - allDayHeight = maxAllAllDayHeight; - } - } - } - mFirstCell = DAY_HEADER_HEIGHT + allDayHeight + ALLDAY_TOP_MARGIN; - } else { - mSelectionAllday = false; - } - mAlldayHeight = allDayHeight; - - mGridAreaHeight = height - mFirstCell; - - // Set up the expand icon position - int allDayIconWidth = mExpandAlldayDrawable.getIntrinsicWidth(); - mExpandAllDayRect.left = Math.max((mHoursWidth - allDayIconWidth) / 2, - EVENT_ALL_DAY_TEXT_LEFT_MARGIN); - mExpandAllDayRect.right = Math.min(mExpandAllDayRect.left + allDayIconWidth, mHoursWidth - - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN); - mExpandAllDayRect.bottom = mFirstCell - EXPAND_ALL_DAY_BOTTOM_MARGIN; - mExpandAllDayRect.top = mExpandAllDayRect.bottom - - mExpandAlldayDrawable.getIntrinsicHeight(); - - mNumHours = mGridAreaHeight / (mCellHeight + HOUR_GAP); - mEventGeometry.setHourHeight(mCellHeight); - - final long minimumDurationMillis = (long) - (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f)); - Event.computePositions(mEvents, minimumDurationMillis); - - // Compute the top of our reachable view - mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight; - if (DEBUG) { - Log.e(TAG, "mViewStartY: " + mViewStartY); - Log.e(TAG, "mMaxViewStartY: " + mMaxViewStartY); - } - if (mViewStartY > mMaxViewStartY) { - mViewStartY = mMaxViewStartY; - computeFirstHour(); - } - - if (mFirstHour == -1) { - initFirstHour(); - mFirstHourOffset = 0; - } - - // When we change the base date, the number of all-day events may - // change and that changes the cell height. When we switch dates, - // we use the mFirstHourOffset from the previous view, but that may - // be too large for the new view if the cell height is smaller. - if (mFirstHourOffset >= mCellHeight + HOUR_GAP) { - mFirstHourOffset = mCellHeight + HOUR_GAP - 1; - } - mViewStartY = mFirstHour * (mCellHeight + HOUR_GAP) - mFirstHourOffset; - - final int eventAreaWidth = mNumDays * (mCellWidth + DAY_GAP); - //When we get new events we don't want to dismiss the popup unless the event changes - if (mSelectedEvent != null && mLastPopupEventID != mSelectedEvent.id) { - mPopup.dismiss(); - } - mPopup.setWidth(eventAreaWidth - 20); - mPopup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT); - } - - /** - * Initialize the state for another view. The given view is one that has - * its own bitmap and will use an animation to replace the current view. - * The current view and new view are either both Week views or both Day - * views. They differ in their base date. - * - * @param view the view to initialize. - */ - private void initView(DayView view) { - view.setSelectedHour(mSelectionHour); - view.mSelectedEvents.clear(); - view.mComputeSelectedEvents = true; - view.mFirstHour = mFirstHour; - view.mFirstHourOffset = mFirstHourOffset; - view.remeasure(getWidth(), getHeight()); - view.initAllDayHeights(); - - view.setSelectedEvent(null); - view.mPrevSelectedEvent = null; - view.mFirstDayOfWeek = mFirstDayOfWeek; - if (view.mEvents.size() > 0) { - view.mSelectionAllday = mSelectionAllday; - } else { - view.mSelectionAllday = false; - } - - // Redraw the screen so that the selection box will be redrawn. We may - // have scrolled to a different part of the day in some other view - // so the selection box in this view may no longer be visible. - view.recalc(); - } - - /** - * Switch to another view based on what was selected (an event or a free - * slot) and how it was selected (by touch or by trackball). - * - * @param trackBallSelection true if the selection was made using the - * trackball. - */ - private void switchViews(boolean trackBallSelection) { - Event selectedEvent = mSelectedEvent; - - mPopup.dismiss(); - mLastPopupEventID = INVALID_EVENT_ID; - if (mNumDays > 1) { - // This is the Week view. - // With touch, we always switch to Day/Agenda View - // With track ball, if we selected a free slot, then create an event. - // If we selected a specific event, switch to EventInfo view. - if (trackBallSelection) { - if (selectedEvent != null) { - if (mIsAccessibilityEnabled) { - mAccessibilityMgr.interrupt(); - } - } - } - } - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - mScrolling = false; - return super.onKeyUp(keyCode, event); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return super.onKeyDown(keyCode, event); - } - - - @Override - public boolean onHoverEvent(MotionEvent event) { - return true; - } - - private boolean isTouchExplorationEnabled() { - return mIsAccessibilityEnabled && mAccessibilityMgr.isTouchExplorationEnabled(); - } - - private void sendAccessibilityEventAsNeeded(boolean speakEvents) { - if (!mIsAccessibilityEnabled) { - return; - } - boolean dayChanged = mLastSelectionDayForAccessibility != mSelectionDayForAccessibility; - boolean hourChanged = mLastSelectionHourForAccessibility != mSelectionHourForAccessibility; - if (dayChanged || hourChanged || - mLastSelectedEventForAccessibility != mSelectedEventForAccessibility) { - mLastSelectionDayForAccessibility = mSelectionDayForAccessibility; - mLastSelectionHourForAccessibility = mSelectionHourForAccessibility; - mLastSelectedEventForAccessibility = mSelectedEventForAccessibility; - - StringBuilder b = new StringBuilder(); - - // Announce only the changes i.e. day or hour or both - if (dayChanged) { - b.append(getSelectedTimeForAccessibility().format("%A ")); - } - if (hourChanged) { - b.append(getSelectedTimeForAccessibility().format(mIs24HourFormat ? "%k" : "%l%p")); - } - if (dayChanged || hourChanged) { - b.append(PERIOD_SPACE); - } - - if (speakEvents) { - if (mEventCountTemplate == null) { - mEventCountTemplate = mContext.getString(R.string.template_announce_item_index); - } - - // Read out the relevant event(s) - int numEvents = mSelectedEvents.size(); - if (numEvents > 0) { - if (mSelectedEventForAccessibility == null) { - // Read out all the events - int i = 1; - for (Event calEvent : mSelectedEvents) { - if (numEvents > 1) { - // Read out x of numEvents if there are more than one event - mStringBuilder.setLength(0); - b.append(mFormatter.format(mEventCountTemplate, i++, numEvents)); - b.append(" "); - } - appendEventAccessibilityString(b, calEvent); - } - } else { - if (numEvents > 1) { - // Read out x of numEvents if there are more than one event - mStringBuilder.setLength(0); - b.append(mFormatter.format(mEventCountTemplate, mSelectedEvents - .indexOf(mSelectedEventForAccessibility) + 1, numEvents)); - b.append(" "); - } - appendEventAccessibilityString(b, mSelectedEventForAccessibility); - } - } - } - - if (dayChanged || hourChanged || speakEvents) { - AccessibilityEvent event = AccessibilityEvent - .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED); - CharSequence msg = b.toString(); - event.getText().add(msg); - event.setAddedCount(msg.length()); - sendAccessibilityEventUnchecked(event); - } - } - } - - /** - * @param b - * @param calEvent - */ - private void appendEventAccessibilityString(StringBuilder b, Event calEvent) { - b.append(calEvent.getTitleAndLocation()); - b.append(PERIOD_SPACE); - String when; - int flags = DateUtils.FORMAT_SHOW_DATE; - if (calEvent.allDay) { - flags |= DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_WEEKDAY; - } else { - flags |= DateUtils.FORMAT_SHOW_TIME; - if (DateFormat.is24HourFormat(mContext)) { - flags |= DateUtils.FORMAT_24HOUR; - } - } - when = Utils.formatDateRange(mContext, calEvent.startMillis, calEvent.endMillis, flags); - b.append(when); - b.append(PERIOD_SPACE); - } - - private class GotoBroadcaster implements Animation.AnimationListener { - private final int mCounter; - private final Time mStart; - private final Time mEnd; - - public GotoBroadcaster(Time start, Time end) { - mCounter = ++sCounter; - mStart = start; - mEnd = end; - } - - @Override - public void onAnimationEnd(Animation animation) { - DayView view = (DayView) mViewSwitcher.getCurrentView(); - view.mViewStartX = 0; - view = (DayView) mViewSwitcher.getNextView(); - view.mViewStartX = 0; - - if (mCounter == sCounter) { - mController.sendEvent(this, EventType.GO_TO, mStart, mEnd, null, -1, - ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null); - } - } - - @Override - public void onAnimationRepeat(Animation animation) { - } - - @Override - public void onAnimationStart(Animation animation) { - } - } - - private View switchViews(boolean forward, float xOffSet, float width, float velocity) { - mAnimationDistance = width - xOffSet; - if (DEBUG) { - Log.d(TAG, "switchViews(" + forward + ") O:" + xOffSet + " Dist:" + mAnimationDistance); - } - - float progress = Math.abs(xOffSet) / width; - if (progress > 1.0f) { - progress = 1.0f; - } - - float inFromXValue, inToXValue; - float outFromXValue, outToXValue; - if (forward) { - inFromXValue = 1.0f - progress; - inToXValue = 0.0f; - outFromXValue = -progress; - outToXValue = -1.0f; - } else { - inFromXValue = progress - 1.0f; - inToXValue = 0.0f; - outFromXValue = progress; - outToXValue = 1.0f; - } - - final Time start = new Time(mBaseDate.timezone); - start.set(mController.getTime()); - if (forward) { - start.monthDay += mNumDays; - } else { - start.monthDay -= mNumDays; - } - mController.setTime(start.normalize(true)); - - Time newSelected = start; - - if (mNumDays == 7) { - newSelected = new Time(start); - adjustToBeginningOfWeek(start); - } - - final Time end = new Time(start); - end.monthDay += mNumDays - 1; - - // 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( - Animation.RELATIVE_TO_SELF, inFromXValue, - Animation.RELATIVE_TO_SELF, inToXValue, - Animation.ABSOLUTE, 0.0f, - Animation.ABSOLUTE, 0.0f); - - TranslateAnimation outAnimation = new TranslateAnimation( - Animation.RELATIVE_TO_SELF, outFromXValue, - Animation.RELATIVE_TO_SELF, outToXValue, - Animation.ABSOLUTE, 0.0f, - Animation.ABSOLUTE, 0.0f); - - long duration = calculateDuration(width - Math.abs(xOffSet), width, velocity); - inAnimation.setDuration(duration); - inAnimation.setInterpolator(mHScrollInterpolator); - outAnimation.setInterpolator(mHScrollInterpolator); - outAnimation.setDuration(duration); - outAnimation.setAnimationListener(new GotoBroadcaster(start, end)); - mViewSwitcher.setInAnimation(inAnimation); - mViewSwitcher.setOutAnimation(outAnimation); - - DayView view = (DayView) mViewSwitcher.getCurrentView(); - view.cleanup(); - mViewSwitcher.showNext(); - view = (DayView) mViewSwitcher.getCurrentView(); - view.setSelected(newSelected, true, false); - view.requestFocus(); - view.reloadEvents(); - view.updateTitle(); - view.restartCurrentTimeUpdates(); - - return view; - } - - // This is called after scrolling stops to move the selected hour - // to the visible part of the screen. - private void resetSelectedHour() { - if (mSelectionHour < mFirstHour + 1) { - setSelectedHour(mFirstHour + 1); - setSelectedEvent(null); - mSelectedEvents.clear(); - mComputeSelectedEvents = true; - } else if (mSelectionHour > mFirstHour + mNumHours - 3) { - setSelectedHour(mFirstHour + mNumHours - 3); - setSelectedEvent(null); - mSelectedEvents.clear(); - mComputeSelectedEvents = true; - } - } - - private void initFirstHour() { - mFirstHour = mSelectionHour - mNumHours / 5; - if (mFirstHour < 0) { - mFirstHour = 0; - } else if (mFirstHour + mNumHours > 24) { - mFirstHour = 24 - mNumHours; - } - } - - /** - * Recomputes the first full hour that is visible on screen after the - * screen is scrolled. - */ - private void computeFirstHour() { - // Compute the first full hour that is visible on screen - mFirstHour = (mViewStartY + mCellHeight + HOUR_GAP - 1) / (mCellHeight + HOUR_GAP); - mFirstHourOffset = mFirstHour * (mCellHeight + HOUR_GAP) - mViewStartY; - } - - private void adjustHourSelection() { - if (mSelectionHour < 0) { - setSelectedHour(0); - if (mMaxAlldayEvents > 0) { - mPrevSelectedEvent = null; - mSelectionAllday = true; - } - } - - if (mSelectionHour > 23) { - setSelectedHour(23); - } - - // If the selected hour is at least 2 time slots from the top and - // bottom of the screen, then don't scroll the view. - if (mSelectionHour < mFirstHour + 1) { - // If there are all-days events for the selected day but there - // are no more normal events earlier in the day, then jump to - // the all-day event area. - // Exception 1: allow the user to scroll to 8am with the trackball - // before jumping to the all-day event area. - // Exception 2: if 12am is on screen, then allow the user to select - // 12am before going up to the all-day event area. - int daynum = mSelectionDay - mFirstJulianDay; - if (daynum < mEarliestStartHour.length && daynum >= 0 - && mMaxAlldayEvents > 0 - && mEarliestStartHour[daynum] > mSelectionHour - && mFirstHour > 0 && mFirstHour < 8) { - mPrevSelectedEvent = null; - mSelectionAllday = true; - setSelectedHour(mFirstHour + 1); - return; - } - - if (mFirstHour > 0) { - mFirstHour -= 1; - mViewStartY -= (mCellHeight + HOUR_GAP); - if (mViewStartY < 0) { - mViewStartY = 0; - } - return; - } - } - - if (mSelectionHour > mFirstHour + mNumHours - 3) { - if (mFirstHour < 24 - mNumHours) { - mFirstHour += 1; - mViewStartY += (mCellHeight + HOUR_GAP); - if (mViewStartY > mMaxViewStartY) { - mViewStartY = mMaxViewStartY; - } - return; - } else if (mFirstHour == 24 - mNumHours && mFirstHourOffset > 0) { - mViewStartY = mMaxViewStartY; - } - } - } - - void clearCachedEvents() { - mLastReloadMillis = 0; - } - - private final Runnable mCancelCallback = new Runnable() { - public void run() { - clearCachedEvents(); - } - }; - - /* package */ void reloadEvents() { - // Protect against this being called before this view has been - // initialized. -// if (mContext == null) { -// return; -// } - - // Make sure our time zones are up to date - mTZUpdater.run(); - - setSelectedEvent(null); - mPrevSelectedEvent = null; - mSelectedEvents.clear(); - - // The start date is the beginning of the week at 12am - Time weekStart = new Time(Utils.getTimeZone(mContext, mTZUpdater)); - weekStart.set(mBaseDate); - weekStart.hour = 0; - weekStart.minute = 0; - weekStart.second = 0; - long millis = weekStart.normalize(true /* ignore isDst */); - - // Avoid reloading events unnecessarily. - if (millis == mLastReloadMillis) { - return; - } - mLastReloadMillis = millis; - - // load events in the background -// mContext.startProgressSpinner(); - final ArrayList events = new ArrayList(); - mEventLoader.loadEventsInBackground(mNumDays, events, mFirstJulianDay, new Runnable() { - - public void run() { - boolean fadeinEvents = mFirstJulianDay != mLoadedFirstJulianDay; - mEvents = events; - mLoadedFirstJulianDay = mFirstJulianDay; - if (mAllDayEvents == null) { - mAllDayEvents = new ArrayList(); - } else { - mAllDayEvents.clear(); - } - - // Create a shorter array for all day events - for (Event e : events) { - if (e.drawAsAllday()) { - mAllDayEvents.add(e); - } - } - - // New events, new layouts - if (mLayouts == null || mLayouts.length < events.size()) { - mLayouts = new StaticLayout[events.size()]; - } else { - Arrays.fill(mLayouts, null); - } - - if (mAllDayLayouts == null || mAllDayLayouts.length < mAllDayEvents.size()) { - mAllDayLayouts = new StaticLayout[events.size()]; - } else { - Arrays.fill(mAllDayLayouts, null); - } - - computeEventRelations(); - - mRemeasure = true; - mComputeSelectedEvents = true; - recalc(); - - // Start animation to cross fade the events - if (fadeinEvents) { - if (mEventsCrossFadeAnimation == null) { - mEventsCrossFadeAnimation = - ObjectAnimator.ofInt(DayView.this, "EventsAlpha", 0, 255); - mEventsCrossFadeAnimation.setDuration(EVENTS_CROSS_FADE_DURATION); - } - mEventsCrossFadeAnimation.start(); - } else{ - invalidate(); - } - } - }, mCancelCallback); - } - - public void setEventsAlpha(int alpha) { - mEventsAlpha = alpha; - invalidate(); - } - - public int getEventsAlpha() { - return mEventsAlpha; - } - - public void stopEventsAnimation() { - if (mEventsCrossFadeAnimation != null) { - mEventsCrossFadeAnimation.cancel(); - } - mEventsAlpha = 255; - } - - private void computeEventRelations() { - // Compute the layout relation between each event before measuring cell - // width, as the cell width should be adjusted along with the relation. - // - // Examples: A (1:00pm - 1:01pm), B (1:02pm - 2:00pm) - // We should mark them as "overwapped". Though they are not overwapped logically, but - // minimum cell height implicitly expands the cell height of A and it should look like - // (1:00pm - 1:15pm) after the cell height adjustment. - - // Compute the space needed for the all-day events, if any. - // Make a pass over all the events, and keep track of the maximum - // number of all-day events in any one day. Also, keep track of - // the earliest event in each day. - int maxAllDayEvents = 0; - final ArrayList events = mEvents; - final int len = events.size(); - // Num of all-day-events on each day. - final int eventsCount[] = new int[mLastJulianDay - mFirstJulianDay + 1]; - Arrays.fill(eventsCount, 0); - for (int ii = 0; ii < len; ii++) { - Event event = events.get(ii); - if (event.startDay > mLastJulianDay || event.endDay < mFirstJulianDay) { - continue; - } - if (event.drawAsAllday()) { - // Count all the events being drawn as allDay events - final int firstDay = Math.max(event.startDay, mFirstJulianDay); - final int lastDay = Math.min(event.endDay, mLastJulianDay); - for (int day = firstDay; day <= lastDay; day++) { - final int count = ++eventsCount[day - mFirstJulianDay]; - if (maxAllDayEvents < count) { - maxAllDayEvents = count; - } - } - - int daynum = event.startDay - mFirstJulianDay; - int durationDays = event.endDay - event.startDay + 1; - if (daynum < 0) { - durationDays += daynum; - daynum = 0; - } - if (daynum + durationDays > mNumDays) { - durationDays = mNumDays - daynum; - } - for (int day = daynum; durationDays > 0; day++, durationDays--) { - mHasAllDayEvent[day] = true; - } - } else { - int daynum = event.startDay - mFirstJulianDay; - int hour = event.startTime / 60; - if (daynum >= 0 && hour < mEarliestStartHour[daynum]) { - mEarliestStartHour[daynum] = hour; - } - - // Also check the end hour in case the event spans more than - // one day. - daynum = event.endDay - mFirstJulianDay; - hour = event.endTime / 60; - if (daynum < mNumDays && hour < mEarliestStartHour[daynum]) { - mEarliestStartHour[daynum] = hour; - } - } - } - mMaxAlldayEvents = maxAllDayEvents; - initAllDayHeights(); - } - - @Override - protected void onDraw(Canvas canvas) { - if (mRemeasure) { - remeasure(getWidth(), getHeight()); - mRemeasure = false; - } - canvas.save(); - - float yTranslate = -mViewStartY + DAY_HEADER_HEIGHT + mAlldayHeight; - // offset canvas by the current drag and header position - canvas.translate(-mViewStartX, yTranslate); - // clip to everything below the allDay area - Rect dest = mDestRect; - dest.top = (int) (mFirstCell - yTranslate); - dest.bottom = (int) (mViewHeight - yTranslate); - dest.left = 0; - dest.right = mViewWidth; - canvas.save(); - canvas.clipRect(dest); - // Draw the movable part of the view - doDraw(canvas); - // restore to having no clip - canvas.restore(); - - if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { - float xTranslate; - if (mViewStartX > 0) { - xTranslate = mViewWidth; - } else { - xTranslate = -mViewWidth; - } - // Move the canvas around to prep it for the next view - // specifically, shift it by a screen and undo the - // yTranslation which will be redone in the nextView's onDraw(). - canvas.translate(xTranslate, -yTranslate); - DayView nextView = (DayView) mViewSwitcher.getNextView(); - - // Prevent infinite recursive calls to onDraw(). - nextView.mTouchMode = TOUCH_MODE_INITIAL_STATE; - - nextView.onDraw(canvas); - // Move it back for this view - canvas.translate(-xTranslate, 0); - } else { - // If we drew another view we already translated it back - // If we didn't draw another view we should be at the edge of the - // screen - canvas.translate(mViewStartX, -yTranslate); - } - - // Draw the fixed areas (that don't scroll) directly to the canvas. - drawAfterScroll(canvas); - if (mComputeSelectedEvents && mUpdateToast) { - mUpdateToast = false; - } - mComputeSelectedEvents = false; - - // Draw overscroll glow - if (!mEdgeEffectTop.isFinished()) { - if (DAY_HEADER_HEIGHT != 0) { - canvas.translate(0, DAY_HEADER_HEIGHT); - } - if (mEdgeEffectTop.draw(canvas)) { - invalidate(); - } - if (DAY_HEADER_HEIGHT != 0) { - canvas.translate(0, -DAY_HEADER_HEIGHT); - } - } - if (!mEdgeEffectBottom.isFinished()) { - canvas.rotate(180, mViewWidth/2, mViewHeight/2); - if (mEdgeEffectBottom.draw(canvas)) { - invalidate(); - } - } - canvas.restore(); - } - - private void drawAfterScroll(Canvas canvas) { - Paint p = mPaint; - Rect r = mRect; - - drawAllDayHighlights(r, canvas, p); - if (mMaxAlldayEvents != 0) { - drawAllDayEvents(mFirstJulianDay, mNumDays, canvas, p); - drawUpperLeftCorner(r, canvas, p); - } - - drawScrollLine(r, canvas, p); - drawDayHeaderLoop(r, canvas, p); - - // Draw the AM and PM indicators if we're in 12 hour mode - if (!mIs24HourFormat) { - drawAmPm(canvas, p); - } - } - - // This isn't really the upper-left corner. It's the square area just - // below the upper-left corner, above the hours and to the left of the - // all-day area. - private void drawUpperLeftCorner(Rect r, Canvas canvas, Paint p) { - setupHourTextPaint(p); - if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { - // Draw the allDay expand/collapse icon - if (mUseExpandIcon) { - mExpandAlldayDrawable.setBounds(mExpandAllDayRect); - mExpandAlldayDrawable.draw(canvas); - } else { - mCollapseAlldayDrawable.setBounds(mExpandAllDayRect); - mCollapseAlldayDrawable.draw(canvas); - } - } - } - - private void drawScrollLine(Rect r, Canvas canvas, Paint p) { - final int right = computeDayLeftPosition(mNumDays); - final int y = mFirstCell - 1; - - p.setAntiAlias(false); - p.setStyle(Style.FILL); - - p.setColor(mCalendarGridLineInnerHorizontalColor); - p.setStrokeWidth(GRID_LINE_INNER_WIDTH); - canvas.drawLine(GRID_LINE_LEFT_MARGIN, y, right, y, p); - p.setAntiAlias(true); - } - - // Computes the x position for the left side of the given day (base 0) - private int computeDayLeftPosition(int day) { - int effectiveWidth = mViewWidth - mHoursWidth; - return day * effectiveWidth / mNumDays + mHoursWidth; - } - - private void drawAllDayHighlights(Rect r, Canvas canvas, Paint p) { - if (mFutureBgColor != 0) { - // First, color the labels area light gray - r.top = 0; - r.bottom = DAY_HEADER_HEIGHT; - r.left = 0; - r.right = mViewWidth; - p.setColor(mBgColor); - p.setStyle(Style.FILL); - canvas.drawRect(r, p); - // and the area that says All day - r.top = DAY_HEADER_HEIGHT; - r.bottom = mFirstCell - 1; - r.left = 0; - r.right = mHoursWidth; - canvas.drawRect(r, p); - - int startIndex = -1; - - int todayIndex = mTodayJulianDay - mFirstJulianDay; - if (todayIndex < 0) { - // Future - startIndex = 0; - } else if (todayIndex >= 1 && todayIndex + 1 < mNumDays) { - // Multiday - tomorrow is visible. - startIndex = todayIndex + 1; - } - - if (startIndex >= 0) { - // Draw the future highlight - r.top = 0; - r.bottom = mFirstCell - 1; - r.left = computeDayLeftPosition(startIndex) + 1; - r.right = computeDayLeftPosition(mNumDays); - p.setColor(mFutureBgColor); - p.setStyle(Style.FILL); - canvas.drawRect(r, p); - } - } - } - - private void drawDayHeaderLoop(Rect r, Canvas canvas, Paint p) { - // Draw the horizontal day background banner - // p.setColor(mCalendarDateBannerBackground); - // r.top = 0; - // r.bottom = DAY_HEADER_HEIGHT; - // r.left = 0; - // r.right = mHoursWidth + mNumDays * (mCellWidth + DAY_GAP); - // canvas.drawRect(r, p); - // - // Fill the extra space on the right side with the default background - // r.left = r.right; - // r.right = mViewWidth; - // p.setColor(mCalendarGridAreaBackground); - // canvas.drawRect(r, p); - if (mNumDays == 1 && ONE_DAY_HEADER_HEIGHT == 0) { - return; - } - - p.setTypeface(mBold); - p.setTextAlign(Paint.Align.RIGHT); - int cell = mFirstJulianDay; - - String[] dayNames; - if (mDateStrWidth < mCellWidth) { - dayNames = mDayStrs; - } else { - dayNames = mDayStrs2Letter; - } - - p.setAntiAlias(true); - for (int day = 0; day < mNumDays; day++, cell++) { - int dayOfWeek = day + mFirstVisibleDayOfWeek; - if (dayOfWeek >= 14) { - dayOfWeek -= 14; - } - - int color = mCalendarDateBannerTextColor; - if (mNumDays == 1) { - if (dayOfWeek == Time.SATURDAY) { - color = mWeek_saturdayColor; - } else if (dayOfWeek == Time.SUNDAY) { - color = mWeek_sundayColor; - } - } else { - final int column = day % 7; - if (Utils.isSaturday(column, mFirstDayOfWeek)) { - color = mWeek_saturdayColor; - } else if (Utils.isSunday(column, mFirstDayOfWeek)) { - color = mWeek_sundayColor; - } - } - - p.setColor(color); - drawDayHeader(dayNames[dayOfWeek], day, cell, canvas, p); - } - p.setTypeface(null); - } - - private void drawAmPm(Canvas canvas, Paint p) { - p.setColor(mCalendarAmPmLabel); - p.setTextSize(AMPM_TEXT_SIZE); - p.setTypeface(mBold); - p.setAntiAlias(true); - p.setTextAlign(Paint.Align.RIGHT); - String text = mAmString; - if (mFirstHour >= 12) { - text = mPmString; - } - int y = mFirstCell + mFirstHourOffset + 2 * mHoursTextHeight + HOUR_GAP; - canvas.drawText(text, HOURS_LEFT_MARGIN, y, p); - - if (mFirstHour < 12 && mFirstHour + mNumHours > 12) { - // Also draw the "PM" - text = mPmString; - y = mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP) - + 2 * mHoursTextHeight + HOUR_GAP; - canvas.drawText(text, HOURS_LEFT_MARGIN, y, p); - } - } - - private void drawCurrentTimeLine(Rect r, final int day, final int top, Canvas canvas, - Paint p) { - r.left = computeDayLeftPosition(day) - CURRENT_TIME_LINE_SIDE_BUFFER + 1; - r.right = computeDayLeftPosition(day + 1) + CURRENT_TIME_LINE_SIDE_BUFFER + 1; - - r.top = top - CURRENT_TIME_LINE_TOP_OFFSET; - r.bottom = r.top + mCurrentTimeLine.getIntrinsicHeight(); - - mCurrentTimeLine.setBounds(r); - mCurrentTimeLine.draw(canvas); - if (mAnimateToday) { - mCurrentTimeAnimateLine.setBounds(r); - mCurrentTimeAnimateLine.setAlpha(mAnimateTodayAlpha); - mCurrentTimeAnimateLine.draw(canvas); - } - } - - private void doDraw(Canvas canvas) { - Paint p = mPaint; - Rect r = mRect; - - if (mFutureBgColor != 0) { - drawBgColors(r, canvas, p); - } - drawGridBackground(r, canvas, p); - drawHours(r, canvas, p); - - // Draw each day - int cell = mFirstJulianDay; - p.setAntiAlias(false); - int alpha = p.getAlpha(); - p.setAlpha(mEventsAlpha); - for (int day = 0; day < mNumDays; day++, cell++) { - // TODO Wow, this needs cleanup. drawEvents loop through all the - // events on every call. - drawEvents(cell, day, HOUR_GAP, canvas, p); - // If this is today - if (cell == mTodayJulianDay) { - int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP) - + ((mCurrentTime.minute * mCellHeight) / 60) + 1; - - // And the current time shows up somewhere on the screen - if (lineY >= mViewStartY && lineY < mViewStartY + mViewHeight - 2) { - drawCurrentTimeLine(r, day, lineY, canvas, p); - } - } - } - p.setAntiAlias(true); - p.setAlpha(alpha); - } - - private void drawHours(Rect r, Canvas canvas, Paint p) { - setupHourTextPaint(p); - - int y = HOUR_GAP + mHoursTextHeight + HOURS_TOP_MARGIN; - - for (int i = 0; i < 24; i++) { - String time = mHourStrs[i]; - canvas.drawText(time, HOURS_LEFT_MARGIN, y, p); - y += mCellHeight + HOUR_GAP; - } - } - - private void setupHourTextPaint(Paint p) { - p.setColor(mCalendarHourLabelColor); - p.setTextSize(HOURS_TEXT_SIZE); - p.setTypeface(Typeface.DEFAULT); - p.setTextAlign(Paint.Align.RIGHT); - p.setAntiAlias(true); - } - - private void drawDayHeader(String dayStr, int day, int cell, Canvas canvas, Paint p) { - int dateNum = mFirstVisibleDate + day; - int x; - if (dateNum > mMonthLength) { - dateNum -= mMonthLength; - } - p.setAntiAlias(true); - - int todayIndex = mTodayJulianDay - mFirstJulianDay; - // Draw day of the month - String dateNumStr = String.valueOf(dateNum); - if (mNumDays > 1) { - float y = DAY_HEADER_HEIGHT - DAY_HEADER_BOTTOM_MARGIN; - - // Draw day of the month - x = computeDayLeftPosition(day + 1) - DAY_HEADER_RIGHT_MARGIN; - p.setTextAlign(Align.RIGHT); - p.setTextSize(DATE_HEADER_FONT_SIZE); - - p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT); - canvas.drawText(dateNumStr, x, y, p); - - // Draw day of the week - x -= p.measureText(" " + dateNumStr); - p.setTextSize(DAY_HEADER_FONT_SIZE); - p.setTypeface(Typeface.DEFAULT); - canvas.drawText(dayStr, x, y, p); - } else { - float y = ONE_DAY_HEADER_HEIGHT - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN; - p.setTextAlign(Align.LEFT); - - - // Draw day of the week - x = computeDayLeftPosition(day) + DAY_HEADER_ONE_DAY_LEFT_MARGIN; - p.setTextSize(DAY_HEADER_FONT_SIZE); - p.setTypeface(Typeface.DEFAULT); - canvas.drawText(dayStr, x, y, p); - - // Draw day of the month - x += p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN; - p.setTextSize(DATE_HEADER_FONT_SIZE); - p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT); - canvas.drawText(dateNumStr, x, y, p); - } - } - - private void drawGridBackground(Rect r, Canvas canvas, Paint p) { - Paint.Style savedStyle = p.getStyle(); - - final float stopX = computeDayLeftPosition(mNumDays); - float y = 0; - final float deltaY = mCellHeight + HOUR_GAP; - int linesIndex = 0; - final float startY = 0; - final float stopY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP); - float x = mHoursWidth; - - // Draw the inner horizontal grid lines - p.setColor(mCalendarGridLineInnerHorizontalColor); - p.setStrokeWidth(GRID_LINE_INNER_WIDTH); - p.setAntiAlias(false); - y = 0; - linesIndex = 0; - for (int hour = 0; hour <= 24; hour++) { - mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN; - mLines[linesIndex++] = y; - mLines[linesIndex++] = stopX; - mLines[linesIndex++] = y; - y += deltaY; - } - if (mCalendarGridLineInnerVerticalColor != mCalendarGridLineInnerHorizontalColor) { - canvas.drawLines(mLines, 0, linesIndex, p); - linesIndex = 0; - p.setColor(mCalendarGridLineInnerVerticalColor); - } - - // Draw the inner vertical grid lines - for (int day = 0; day <= mNumDays; day++) { - x = computeDayLeftPosition(day); - mLines[linesIndex++] = x; - mLines[linesIndex++] = startY; - mLines[linesIndex++] = x; - mLines[linesIndex++] = stopY; - } - canvas.drawLines(mLines, 0, linesIndex, p); - - // Restore the saved style. - p.setStyle(savedStyle); - p.setAntiAlias(true); - } - - /** - * @param r - * @param canvas - * @param p - */ - private void drawBgColors(Rect r, Canvas canvas, Paint p) { - int todayIndex = mTodayJulianDay - mFirstJulianDay; - // Draw the hours background color - r.top = mDestRect.top; - r.bottom = mDestRect.bottom; - r.left = 0; - r.right = mHoursWidth; - p.setColor(mBgColor); - p.setStyle(Style.FILL); - p.setAntiAlias(false); - canvas.drawRect(r, p); - - // Draw background for grid area - if (mNumDays == 1 && todayIndex == 0) { - // Draw a white background for the time later than current time - int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP) - + ((mCurrentTime.minute * mCellHeight) / 60) + 1; - if (lineY < mViewStartY + mViewHeight) { - lineY = Math.max(lineY, mViewStartY); - r.left = mHoursWidth; - r.right = mViewWidth; - r.top = lineY; - r.bottom = mViewStartY + mViewHeight; - p.setColor(mFutureBgColor); - canvas.drawRect(r, p); - } - } else if (todayIndex >= 0 && todayIndex < mNumDays) { - // Draw today with a white background for the time later than current time - int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP) - + ((mCurrentTime.minute * mCellHeight) / 60) + 1; - if (lineY < mViewStartY + mViewHeight) { - lineY = Math.max(lineY, mViewStartY); - r.left = computeDayLeftPosition(todayIndex) + 1; - r.right = computeDayLeftPosition(todayIndex + 1); - r.top = lineY; - r.bottom = mViewStartY + mViewHeight; - p.setColor(mFutureBgColor); - canvas.drawRect(r, p); - } - - // Paint Tomorrow and later days with future color - if (todayIndex + 1 < mNumDays) { - r.left = computeDayLeftPosition(todayIndex + 1) + 1; - r.right = computeDayLeftPosition(mNumDays); - r.top = mDestRect.top; - r.bottom = mDestRect.bottom; - p.setColor(mFutureBgColor); - canvas.drawRect(r, p); - } - } else if (todayIndex < 0) { - // Future - r.left = computeDayLeftPosition(0) + 1; - r.right = computeDayLeftPosition(mNumDays); - r.top = mDestRect.top; - r.bottom = mDestRect.bottom; - p.setColor(mFutureBgColor); - canvas.drawRect(r, p); - } - p.setAntiAlias(true); - } - - private int computeMaxStringWidth(int currentMax, String[] strings, Paint p) { - float maxWidthF = 0.0f; - - int len = strings.length; - for (int i = 0; i < len; i++) { - float width = p.measureText(strings[i]); - maxWidthF = Math.max(width, maxWidthF); - } - int maxWidth = (int) (maxWidthF + 0.5); - if (maxWidth < currentMax) { - maxWidth = currentMax; - } - return maxWidth; - } - - private void saveSelectionPosition(float left, float top, float right, float bottom) { - mPrevBox.left = (int) left; - mPrevBox.right = (int) right; - mPrevBox.top = (int) top; - mPrevBox.bottom = (int) bottom; - } - - private void setupTextRect(Rect r) { - if (r.bottom <= r.top || r.right <= r.left) { - r.bottom = r.top; - r.right = r.left; - return; - } - - if (r.bottom - r.top > EVENT_TEXT_TOP_MARGIN + EVENT_TEXT_BOTTOM_MARGIN) { - r.top += EVENT_TEXT_TOP_MARGIN; - r.bottom -= EVENT_TEXT_BOTTOM_MARGIN; - } - if (r.right - r.left > EVENT_TEXT_LEFT_MARGIN + EVENT_TEXT_RIGHT_MARGIN) { - r.left += EVENT_TEXT_LEFT_MARGIN; - r.right -= EVENT_TEXT_RIGHT_MARGIN; - } - } - - private void setupAllDayTextRect(Rect r) { - if (r.bottom <= r.top || r.right <= r.left) { - r.bottom = r.top; - r.right = r.left; - return; - } - - if (r.bottom - r.top > EVENT_ALL_DAY_TEXT_TOP_MARGIN + EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN) { - r.top += EVENT_ALL_DAY_TEXT_TOP_MARGIN; - r.bottom -= EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN; - } - if (r.right - r.left > EVENT_ALL_DAY_TEXT_LEFT_MARGIN + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN) { - r.left += EVENT_ALL_DAY_TEXT_LEFT_MARGIN; - r.right -= EVENT_ALL_DAY_TEXT_RIGHT_MARGIN; - } - } - - /** - * Return the layout for a numbered event. Create it if not already existing - */ - private StaticLayout getEventLayout(StaticLayout[] layouts, int i, Event event, Paint paint, - Rect r) { - if (i < 0 || i >= layouts.length) { - return null; - } - - StaticLayout layout = layouts[i]; - // Check if we have already initialized the StaticLayout and that - // the width hasn't changed (due to vertical resizing which causes - // re-layout of events at min height) - if (layout == null || r.width() != layout.getWidth()) { - SpannableStringBuilder bob = new SpannableStringBuilder(); - if (event.title != null) { - // MAX - 1 since we add a space - bob.append(drawTextSanitizer(event.title.toString(), MAX_EVENT_TEXT_LEN - 1)); - bob.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length(), 0); - bob.append(' '); - } - if (event.location != null) { - bob.append(drawTextSanitizer(event.location.toString(), - MAX_EVENT_TEXT_LEN - bob.length())); - } - - switch (event.selfAttendeeStatus) { - case Attendees.ATTENDEE_STATUS_INVITED: - paint.setColor(event.color); - break; - case Attendees.ATTENDEE_STATUS_DECLINED: - paint.setColor(mEventTextColor); - paint.setAlpha(Utils.DECLINED_EVENT_TEXT_ALPHA); - break; - case Attendees.ATTENDEE_STATUS_NONE: // Your own events - case Attendees.ATTENDEE_STATUS_ACCEPTED: - case Attendees.ATTENDEE_STATUS_TENTATIVE: - default: - paint.setColor(mEventTextColor); - break; - } - - // Leave a one pixel boundary on the left and right of the rectangle for the event - layout = new StaticLayout(bob, 0, bob.length(), new TextPaint(paint), r.width(), - Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null, r.width()); - - layouts[i] = layout; - } - layout.getPaint().setAlpha(mEventsAlpha); - return layout; - } - - private void drawAllDayEvents(int firstDay, int numDays, Canvas canvas, Paint p) { - - p.setTextSize(NORMAL_FONT_SIZE); - p.setTextAlign(Paint.Align.LEFT); - Paint eventTextPaint = mEventTextPaint; - - final float startY = DAY_HEADER_HEIGHT; - final float stopY = startY + mAlldayHeight + ALLDAY_TOP_MARGIN; - float x = 0; - int linesIndex = 0; - - // Draw the inner vertical grid lines - p.setColor(mCalendarGridLineInnerVerticalColor); - x = mHoursWidth; - p.setStrokeWidth(GRID_LINE_INNER_WIDTH); - // Line bounding the top of the all day area - mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN; - mLines[linesIndex++] = startY; - mLines[linesIndex++] = computeDayLeftPosition(mNumDays); - mLines[linesIndex++] = startY; - - for (int day = 0; day <= mNumDays; day++) { - x = computeDayLeftPosition(day); - mLines[linesIndex++] = x; - mLines[linesIndex++] = startY; - mLines[linesIndex++] = x; - mLines[linesIndex++] = stopY; - } - p.setAntiAlias(false); - canvas.drawLines(mLines, 0, linesIndex, p); - p.setStyle(Style.FILL); - - int y = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN; - int lastDay = firstDay + numDays - 1; - final ArrayList events = mAllDayEvents; - int numEvents = events.size(); - // Whether or not we should draw the more events text - boolean hasMoreEvents = false; - // size of the allDay area - float drawHeight = mAlldayHeight; - // max number of events being drawn in one day of the allday area - float numRectangles = mMaxAlldayEvents; - // Where to cut off drawn allday events - int allDayEventClip = DAY_HEADER_HEIGHT + mAlldayHeight + ALLDAY_TOP_MARGIN; - // The number of events that weren't drawn in each day - mSkippedAlldayEvents = new int[numDays]; - if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount && !mShowAllAllDayEvents && - mAnimateDayHeight == 0) { - // We draw one fewer event than will fit so that more events text - // can be drawn - numRectangles = mMaxUnexpandedAlldayEventCount - 1; - // We also clip the events above the more events text - allDayEventClip -= MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; - hasMoreEvents = true; - } else if (mAnimateDayHeight != 0) { - // clip at the end of the animating space - allDayEventClip = DAY_HEADER_HEIGHT + mAnimateDayHeight + ALLDAY_TOP_MARGIN; - } - - int alpha = eventTextPaint.getAlpha(); - eventTextPaint.setAlpha(mEventsAlpha); - for (int i = 0; i < numEvents; i++) { - Event event = events.get(i); - int startDay = event.startDay; - int endDay = event.endDay; - if (startDay > lastDay || endDay < firstDay) { - continue; - } - if (startDay < firstDay) { - startDay = firstDay; - } - if (endDay > lastDay) { - endDay = lastDay; - } - int startIndex = startDay - firstDay; - int endIndex = endDay - firstDay; - float height = mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount ? mAnimateDayEventHeight : - drawHeight / numRectangles; - - // Prevent a single event from getting too big - if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) { - height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; - } - - // Leave a one-pixel space between the vertical day lines and the - // event rectangle. - event.left = computeDayLeftPosition(startIndex); - event.right = computeDayLeftPosition(endIndex + 1) - DAY_GAP; - event.top = y + height * event.getColumn(); - event.bottom = event.top + height - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN; - if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { - // check if we should skip this event. We skip if it starts - // after the clip bound or ends after the skip bound and we're - // not animating. - if (event.top >= allDayEventClip) { - incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex); - continue; - } else if (event.bottom > allDayEventClip) { - if (hasMoreEvents) { - incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex); - continue; - } - event.bottom = allDayEventClip; - } - } - Rect r = drawEventRect(event, canvas, p, eventTextPaint, (int) event.top, - (int) event.bottom); - setupAllDayTextRect(r); - StaticLayout layout = getEventLayout(mAllDayLayouts, i, event, eventTextPaint, r); - drawEventText(layout, r, canvas, r.top, r.bottom, true); - - // Check if this all-day event intersects the selected day - if (mSelectionAllday && mComputeSelectedEvents) { - if (startDay <= mSelectionDay && endDay >= mSelectionDay) { - mSelectedEvents.add(event); - } - } - } - eventTextPaint.setAlpha(alpha); - - if (mMoreAlldayEventsTextAlpha != 0 && mSkippedAlldayEvents != null) { - // If the more allday text should be visible, draw it. - alpha = p.getAlpha(); - p.setAlpha(mEventsAlpha); - p.setColor(mMoreAlldayEventsTextAlpha << 24 & mMoreEventsTextColor); - for (int i = 0; i < mSkippedAlldayEvents.length; i++) { - if (mSkippedAlldayEvents[i] > 0) { - drawMoreAlldayEvents(canvas, mSkippedAlldayEvents[i], i, p); - } - } - p.setAlpha(alpha); - } - - if (mSelectionAllday) { - // Compute the neighbors for the list of all-day events that - // intersect the selected day. - computeAllDayNeighbors(); - - // Set the selection position to zero so that when we move down - // to the normal event area, we will highlight the topmost event. - saveSelectionPosition(0f, 0f, 0f, 0f); - } - } - - // Helper method for counting the number of allday events skipped on each day - private void incrementSkipCount(int[] counts, int startIndex, int endIndex) { - if (counts == null || startIndex < 0 || endIndex > counts.length) { - return; - } - for (int i = startIndex; i <= endIndex; i++) { - counts[i]++; - } - } - - // Draws the "box +n" text for hidden allday events - protected void drawMoreAlldayEvents(Canvas canvas, int remainingEvents, int day, Paint p) { - int x = computeDayLeftPosition(day) + EVENT_ALL_DAY_TEXT_LEFT_MARGIN; - int y = (int) (mAlldayHeight - .5f * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - .5f - * EVENT_SQUARE_WIDTH + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN); - Rect r = mRect; - r.top = y; - r.left = x; - r.bottom = y + EVENT_SQUARE_WIDTH; - r.right = x + EVENT_SQUARE_WIDTH; - p.setColor(mMoreEventsTextColor); - p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH); - p.setStyle(Style.STROKE); - p.setAntiAlias(false); - canvas.drawRect(r, p); - p.setAntiAlias(true); - p.setStyle(Style.FILL); - p.setTextSize(EVENT_TEXT_FONT_SIZE); - String text = mResources.getQuantityString(R.plurals.month_more_events, remainingEvents); - y += EVENT_SQUARE_WIDTH; - x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING; - canvas.drawText(String.format(text, remainingEvents), x, y, p); - } - - private void computeAllDayNeighbors() { - int len = mSelectedEvents.size(); - if (len == 0 || mSelectedEvent != null) { - return; - } - - // First, clear all the links - for (int ii = 0; ii < len; ii++) { - Event ev = mSelectedEvents.get(ii); - ev.nextUp = null; - ev.nextDown = null; - ev.nextLeft = null; - ev.nextRight = null; - } - - // For each event in the selected event list "mSelectedEvents", find - // its neighbors in the up and down directions. This could be done - // more efficiently by sorting on the Event.getColumn() field, but - // the list is expected to be very small. - - // Find the event in the same row as the previously selected all-day - // event, if any. - int startPosition = -1; - if (mPrevSelectedEvent != null && mPrevSelectedEvent.drawAsAllday()) { - startPosition = mPrevSelectedEvent.getColumn(); - } - int maxPosition = -1; - Event startEvent = null; - Event maxPositionEvent = null; - for (int ii = 0; ii < len; ii++) { - Event ev = mSelectedEvents.get(ii); - int position = ev.getColumn(); - if (position == startPosition) { - startEvent = ev; - } else if (position > maxPosition) { - maxPositionEvent = ev; - maxPosition = position; - } - for (int jj = 0; jj < len; jj++) { - if (jj == ii) { - continue; - } - Event neighbor = mSelectedEvents.get(jj); - int neighborPosition = neighbor.getColumn(); - if (neighborPosition == position - 1) { - ev.nextUp = neighbor; - } else if (neighborPosition == position + 1) { - ev.nextDown = neighbor; - } - } - } - if (startEvent != null) { - setSelectedEvent(startEvent); - } else { - setSelectedEvent(maxPositionEvent); - } - } - - private void drawEvents(int date, int dayIndex, int top, Canvas canvas, Paint p) { - Paint eventTextPaint = mEventTextPaint; - int left = computeDayLeftPosition(dayIndex) + 1; - int cellWidth = computeDayLeftPosition(dayIndex + 1) - left + 1; - int cellHeight = mCellHeight; - - // Use the selected hour as the selection region - Rect selectionArea = mSelectionRect; - selectionArea.top = top + mSelectionHour * (cellHeight + HOUR_GAP); - selectionArea.bottom = selectionArea.top + cellHeight; - selectionArea.left = left; - selectionArea.right = selectionArea.left + cellWidth; - - final ArrayList events = mEvents; - int numEvents = events.size(); - EventGeometry geometry = mEventGeometry; - - final int viewEndY = mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight; - - int alpha = eventTextPaint.getAlpha(); - eventTextPaint.setAlpha(mEventsAlpha); - for (int i = 0; i < numEvents; i++) { - Event event = events.get(i); - if (!geometry.computeEventRect(date, left, top, cellWidth, event)) { - continue; - } - - // Don't draw it if it is not visible - if (event.bottom < mViewStartY || event.top > viewEndY) { - continue; - } - - if (date == mSelectionDay && !mSelectionAllday && mComputeSelectedEvents - && geometry.eventIntersectsSelection(event, selectionArea)) { - mSelectedEvents.add(event); - } - - Rect r = drawEventRect(event, canvas, p, eventTextPaint, mViewStartY, viewEndY); - setupTextRect(r); - - // Don't draw text if it is not visible - if (r.top > viewEndY || r.bottom < mViewStartY) { - continue; - } - StaticLayout layout = getEventLayout(mLayouts, i, event, eventTextPaint, r); - // TODO: not sure why we are 4 pixels off - drawEventText(layout, r, canvas, mViewStartY + 4, mViewStartY + mViewHeight - - DAY_HEADER_HEIGHT - mAlldayHeight, false); - } - eventTextPaint.setAlpha(alpha); - } - - private Rect drawEventRect(Event event, Canvas canvas, Paint p, Paint eventTextPaint, - int visibleTop, int visibleBot) { - // Draw the Event Rect - Rect r = mRect; - r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN, visibleTop); - r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN, visibleBot); - r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN; - r.right = (int) event.right; - - int color = event.color; - switch (event.selfAttendeeStatus) { - case Attendees.ATTENDEE_STATUS_INVITED: - if (event != mClickedEvent) { - p.setStyle(Style.STROKE); - } - break; - case Attendees.ATTENDEE_STATUS_DECLINED: - if (event != mClickedEvent) { - color = Utils.getDeclinedColorFromColor(color); - } - case Attendees.ATTENDEE_STATUS_NONE: // Your own events - case Attendees.ATTENDEE_STATUS_ACCEPTED: - case Attendees.ATTENDEE_STATUS_TENTATIVE: - default: - p.setStyle(Style.FILL_AND_STROKE); - break; - } - - p.setAntiAlias(false); - - int floorHalfStroke = (int) Math.floor(EVENT_RECT_STROKE_WIDTH / 2.0f); - int ceilHalfStroke = (int) Math.ceil(EVENT_RECT_STROKE_WIDTH / 2.0f); - r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN + floorHalfStroke, visibleTop); - r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN - ceilHalfStroke, - visibleBot); - r.left += floorHalfStroke; - r.right -= ceilHalfStroke; - p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH); - p.setColor(color); - int alpha = p.getAlpha(); - p.setAlpha(mEventsAlpha); - canvas.drawRect(r, p); - p.setAlpha(alpha); - p.setStyle(Style.FILL); - - // Setup rect for drawEventText which follows - r.top = (int) event.top + EVENT_RECT_TOP_MARGIN; - r.bottom = (int) event.bottom - EVENT_RECT_BOTTOM_MARGIN; - r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN; - r.right = (int) event.right - EVENT_RECT_RIGHT_MARGIN; - return r; - } - - private final Pattern drawTextSanitizerFilter = Pattern.compile("[\t\n],"); - - // Sanitize a string before passing it to drawText or else we get little - // squares. For newlines and tabs before a comma, delete the character. - // Otherwise, just replace them with a space. - private String drawTextSanitizer(String string, int maxEventTextLen) { - Matcher m = drawTextSanitizerFilter.matcher(string); - string = m.replaceAll(","); - - int len = string.length(); - if (maxEventTextLen <= 0) { - string = ""; - len = 0; - } else if (len > maxEventTextLen) { - string = string.substring(0, maxEventTextLen); - len = maxEventTextLen; - } - - return string.replace('\n', ' '); - } - - private void drawEventText(StaticLayout eventLayout, Rect rect, Canvas canvas, int top, - int bottom, boolean center) { - // drawEmptyRect(canvas, rect, 0xFFFF00FF); // for debugging - - int width = rect.right - rect.left; - int height = rect.bottom - rect.top; - - // If the rectangle is too small for text, then return - if (eventLayout == null || width < MIN_CELL_WIDTH_FOR_TEXT) { - return; - } - - int totalLineHeight = 0; - int lineCount = eventLayout.getLineCount(); - for (int i = 0; i < lineCount; i++) { - int lineBottom = eventLayout.getLineBottom(i); - if (lineBottom <= height) { - totalLineHeight = lineBottom; - } else { - break; - } - } - - // + 2 is small workaround when the font is slightly bigger then the rect. This will - // still allow the text to be shown without overflowing into the other all day rects. - if (totalLineHeight == 0 || rect.top > bottom || rect.top + totalLineHeight + 2 < top) { - return; - } - - // Use a StaticLayout to format the string. - canvas.save(); - // canvas.translate(rect.left, rect.top + (rect.bottom - rect.top / 2)); - int padding = center? (rect.bottom - rect.top - totalLineHeight) / 2 : 0; - canvas.translate(rect.left, rect.top + padding); - rect.left = 0; - rect.right = width; - rect.top = 0; - rect.bottom = totalLineHeight; - - // There's a bug somewhere. If this rect is outside of a previous - // cliprect, this becomes a no-op. What happens is that the text draw - // past the event rect. The current fix is to not draw the staticLayout - // at all if it is completely out of bound. - canvas.clipRect(rect); - eventLayout.draw(canvas); - canvas.restore(); - } - - // The following routines are called from the parent activity when certain - // touch events occur. - private void doDown(MotionEvent ev) { - mTouchMode = TOUCH_MODE_DOWN; - mViewStartX = 0; - mOnFlingCalled = false; - mHandler.removeCallbacks(mContinueScroll); - int x = (int) ev.getX(); - int y = (int) ev.getY(); - - // Save selection information: we use setSelectionFromPosition to find the selected event - // in order to show the "clicked" color. But since it is also setting the selected info - // for new events, we need to restore the old info after calling the function. - Event oldSelectedEvent = mSelectedEvent; - int oldSelectionDay = mSelectionDay; - int oldSelectionHour = mSelectionHour; - if (setSelectionFromPosition(x, y, false)) { - // If a time was selected (a blue selection box is visible) and the click location - // is in the selected time, do not show a click on an event to prevent a situation - // of both a selection and an event are clicked when they overlap. - boolean pressedSelected = (mSelectionMode != SELECTION_HIDDEN) - && oldSelectionDay == mSelectionDay && oldSelectionHour == mSelectionHour; - if (!pressedSelected && mSelectedEvent != null) { - mSavedClickedEvent = mSelectedEvent; - mDownTouchTime = System.currentTimeMillis(); - postDelayed (mSetClick,mOnDownDelay); - } else { - eventClickCleanup(); - } - } - mSelectedEvent = oldSelectedEvent; - mSelectionDay = oldSelectionDay; - mSelectionHour = oldSelectionHour; - invalidate(); - } - - // Kicks off all the animations when the expand allday area is tapped - private void doExpandAllDayClick() { - mShowAllAllDayEvents = !mShowAllAllDayEvents; - - ObjectAnimator.setFrameDelay(0); - - // Determine the starting height - if (mAnimateDayHeight == 0) { - mAnimateDayHeight = mShowAllAllDayEvents ? - mAlldayHeight - (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT : mAlldayHeight; - } - // Cancel current animations - mCancellingAnimations = true; - if (mAlldayAnimator != null) { - mAlldayAnimator.cancel(); - } - if (mAlldayEventAnimator != null) { - mAlldayEventAnimator.cancel(); - } - if (mMoreAlldayEventsAnimator != null) { - mMoreAlldayEventsAnimator.cancel(); - } - mCancellingAnimations = false; - // get new animators - mAlldayAnimator = getAllDayAnimator(); - mAlldayEventAnimator = getAllDayEventAnimator(); - mMoreAlldayEventsAnimator = ObjectAnimator.ofInt(this, - "moreAllDayEventsTextAlpha", - mShowAllAllDayEvents ? MORE_EVENTS_MAX_ALPHA : 0, - mShowAllAllDayEvents ? 0 : MORE_EVENTS_MAX_ALPHA); - - // Set up delays and start the animators - mAlldayAnimator.setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0); - mAlldayAnimator.start(); - mMoreAlldayEventsAnimator.setStartDelay(mShowAllAllDayEvents ? 0 : ANIMATION_DURATION); - mMoreAlldayEventsAnimator.setDuration(ANIMATION_SECONDARY_DURATION); - mMoreAlldayEventsAnimator.start(); - if (mAlldayEventAnimator != null) { - // This is the only animator that can return null, so check it - mAlldayEventAnimator - .setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0); - mAlldayEventAnimator.start(); - } - } - - /** - * Figures out the initial heights for allDay events and space when - * a view is being set up. - */ - public void initAllDayHeights() { - if (mMaxAlldayEvents <= mMaxUnexpandedAlldayEventCount) { - return; - } - if (mShowAllAllDayEvents) { - int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; - maxADHeight = Math.min(maxADHeight, - (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)); - mAnimateDayEventHeight = maxADHeight / mMaxAlldayEvents; - } else { - mAnimateDayEventHeight = (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; - } - } - - // Sets up an animator for changing the height of allday events - private ObjectAnimator getAllDayEventAnimator() { - // First calculate the absolute max height - int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; - // Now expand to fit but not beyond the absolute max - maxADHeight = - Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)); - // calculate the height of individual events in order to fit - int fitHeight = maxADHeight / mMaxAlldayEvents; - int currentHeight = mAnimateDayEventHeight; - int desiredHeight = - mShowAllAllDayEvents ? fitHeight : (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT; - // if there's nothing to animate just return - if (currentHeight == desiredHeight) { - return null; - } - - // Set up the animator with the calculated values - ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayEventHeight", - currentHeight, desiredHeight); - animator.setDuration(ANIMATION_DURATION); - return animator; - } - - // Sets up an animator for changing the height of the allday area - private ObjectAnimator getAllDayAnimator() { - // Calculate the absolute max height - int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT; - // Find the desired height but don't exceed abs max - maxADHeight = - Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)); - // calculate the current and desired heights - int currentHeight = mAnimateDayHeight != 0 ? mAnimateDayHeight : mAlldayHeight; - int desiredHeight = mShowAllAllDayEvents ? maxADHeight : - (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - 1); - - // Set up the animator with the calculated values - ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayHeight", - currentHeight, desiredHeight); - animator.setDuration(ANIMATION_DURATION); - - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (!mCancellingAnimations) { - // when finished, set this to 0 to signify not animating - mAnimateDayHeight = 0; - mUseExpandIcon = !mShowAllAllDayEvents; - } - mRemeasure = true; - invalidate(); - } - }); - return animator; - } - - // setter for the 'box +n' alpha text used by the animator - public void setMoreAllDayEventsTextAlpha(int alpha) { - mMoreAlldayEventsTextAlpha = alpha; - invalidate(); - } - - // setter for the height of the allday area used by the animator - public void setAnimateDayHeight(int height) { - mAnimateDayHeight = height; - mRemeasure = true; - invalidate(); - } - - // setter for the height of allday events used by the animator - public void setAnimateDayEventHeight(int height) { - mAnimateDayEventHeight = height; - mRemeasure = true; - invalidate(); - } - - private void doSingleTapUp(MotionEvent ev) { - if (!mHandleActionUp || mScrolling) { - return; - } - - int x = (int) ev.getX(); - int y = (int) ev.getY(); - int selectedDay = mSelectionDay; - int selectedHour = mSelectionHour; - - if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { - // check if the tap was in the allday expansion area - int bottom = mFirstCell; - if((x < mHoursWidth && y > DAY_HEADER_HEIGHT && y < DAY_HEADER_HEIGHT + mAlldayHeight) - || (!mShowAllAllDayEvents && mAnimateDayHeight == 0 && y < bottom && - y >= bottom - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)) { - doExpandAllDayClick(); - return; - } - } - - boolean validPosition = setSelectionFromPosition(x, y, false); - if (!validPosition) { - if (y < DAY_HEADER_HEIGHT) { - Time selectedTime = new Time(mBaseDate); - selectedTime.setJulianDay(mSelectionDay); - selectedTime.hour = mSelectionHour; - selectedTime.normalize(true /* ignore isDst */); - mController.sendEvent(this, EventType.GO_TO, null, null, selectedTime, -1, - ViewType.DAY, CalendarController.EXTRA_GOTO_DATE, null, null); - } - return; - } - - boolean hasSelection = mSelectionMode != SELECTION_HIDDEN; - boolean pressedSelected = (hasSelection || mTouchExplorationEnabled) - && selectedDay == mSelectionDay && selectedHour == mSelectionHour; - - if (mSelectedEvent != null) { - // If the tap is on an event, launch the "View event" view - if (mIsAccessibilityEnabled) { - mAccessibilityMgr.interrupt(); - } - - mSelectionMode = SELECTION_HIDDEN; - - int yLocation = - (int)((mSelectedEvent.top + mSelectedEvent.bottom)/2); - // Y location is affected by the position of the event in the scrolling - // view (mViewStartY) and the presence of all day events (mFirstCell) - if (!mSelectedEvent.allDay) { - yLocation += (mFirstCell - mViewStartY); - } - mClickedYLocation = yLocation; - long clearDelay = (CLICK_DISPLAY_DURATION + mOnDownDelay) - - (System.currentTimeMillis() - mDownTouchTime); - if (clearDelay > 0) { - this.postDelayed(mClearClick, clearDelay); - } else { - this.post(mClearClick); - } - } - invalidate(); - } - - private void doLongPress(MotionEvent ev) { - eventClickCleanup(); - if (mScrolling) { - return; - } - - // Scale gesture in progress - if (mStartingSpanY != 0) { - return; - } - - int x = (int) ev.getX(); - int y = (int) ev.getY(); - - boolean validPosition = setSelectionFromPosition(x, y, false); - if (!validPosition) { - // return if the touch wasn't on an area of concern - return; - } - - invalidate(); - performLongClick(); - } - - private void doScroll(MotionEvent e1, MotionEvent e2, float deltaX, float deltaY) { - cancelAnimation(); - if (mStartingScroll) { - mInitialScrollX = 0; - mInitialScrollY = 0; - mStartingScroll = false; - } - - mInitialScrollX += deltaX; - mInitialScrollY += deltaY; - int distanceX = (int) mInitialScrollX; - int distanceY = (int) mInitialScrollY; - - final float focusY = getAverageY(e2); - if (mRecalCenterHour) { - // Calculate the hour that correspond to the average of the Y touch points - mGestureCenterHour = (mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) - / (mCellHeight + DAY_GAP); - mRecalCenterHour = false; - } - - // If we haven't figured out the predominant scroll direction yet, - // then do it now. - if (mTouchMode == TOUCH_MODE_DOWN) { - int absDistanceX = Math.abs(distanceX); - int absDistanceY = Math.abs(distanceY); - mScrollStartY = mViewStartY; - mPreviousDirection = 0; - - if (absDistanceX > absDistanceY) { - int slopFactor = mScaleGestureDetector.isInProgress() ? 20 : 2; - if (absDistanceX > mScaledPagingTouchSlop * slopFactor) { - mTouchMode = TOUCH_MODE_HSCROLL; - mViewStartX = distanceX; - initNextView(-mViewStartX); - } - } else { - mTouchMode = TOUCH_MODE_VSCROLL; - } - } else if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { - // We are already scrolling horizontally, so check if we - // changed the direction of scrolling so that the other week - // is now visible. - mViewStartX = distanceX; - if (distanceX != 0) { - int direction = (distanceX > 0) ? 1 : -1; - if (direction != mPreviousDirection) { - // The user has switched the direction of scrolling - // so re-init the next view - initNextView(-mViewStartX); - mPreviousDirection = direction; - } - } - } - - if ((mTouchMode & TOUCH_MODE_VSCROLL) != 0) { - // Calculate the top of the visible region in the calendar grid. - // Increasing/decrease this will scroll the calendar grid up/down. - mViewStartY = (int) ((mGestureCenterHour * (mCellHeight + DAY_GAP)) - - focusY + DAY_HEADER_HEIGHT + mAlldayHeight); - - // If dragging while already at the end, do a glow - final int pulledToY = (int) (mScrollStartY + deltaY); - if (pulledToY < 0) { - mEdgeEffectTop.onPull(deltaY / mViewHeight); - if (!mEdgeEffectBottom.isFinished()) { - mEdgeEffectBottom.onRelease(); - } - } else if (pulledToY > mMaxViewStartY) { - mEdgeEffectBottom.onPull(deltaY / mViewHeight); - if (!mEdgeEffectTop.isFinished()) { - mEdgeEffectTop.onRelease(); - } - } - - if (mViewStartY < 0) { - mViewStartY = 0; - mRecalCenterHour = true; - } else if (mViewStartY > mMaxViewStartY) { - mViewStartY = mMaxViewStartY; - mRecalCenterHour = true; - } - if (mRecalCenterHour) { - // Calculate the hour that correspond to the average of the Y touch points - mGestureCenterHour = (mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) - / (mCellHeight + DAY_GAP); - mRecalCenterHour = false; - } - computeFirstHour(); - } - - mScrolling = true; - - mSelectionMode = SELECTION_HIDDEN; - invalidate(); - } - - private float getAverageY(MotionEvent me) { - int count = me.getPointerCount(); - float focusY = 0; - for (int i = 0; i < count; i++) { - focusY += me.getY(i); - } - focusY /= count; - return focusY; - } - - private void cancelAnimation() { - Animation in = mViewSwitcher.getInAnimation(); - if (in != null) { - // cancel() doesn't terminate cleanly. - in.scaleCurrentDuration(0); - } - Animation out = mViewSwitcher.getOutAnimation(); - if (out != null) { - // cancel() doesn't terminate cleanly. - out.scaleCurrentDuration(0); - } - } - - private void doFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - cancelAnimation(); - - mSelectionMode = SELECTION_HIDDEN; - eventClickCleanup(); - - mOnFlingCalled = true; - - if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { - // Horizontal fling. - // initNextView(deltaX); - mTouchMode = TOUCH_MODE_INITIAL_STATE; - if (DEBUG) Log.d(TAG, "doFling: velocityX " + velocityX); - int deltaX = (int) e2.getX() - (int) e1.getX(); - switchViews(deltaX < 0, mViewStartX, mViewWidth, velocityX); - mViewStartX = 0; - return; - } - - if ((mTouchMode & TOUCH_MODE_VSCROLL) == 0) { - if (DEBUG) Log.d(TAG, "doFling: no fling"); - return; - } - - // Vertical fling. - mTouchMode = TOUCH_MODE_INITIAL_STATE; - mViewStartX = 0; - - if (DEBUG) { - Log.d(TAG, "doFling: mViewStartY" + mViewStartY + " velocityY " + velocityY); - } - - // Continue scrolling vertically - mScrolling = true; - mScroller.fling(0 /* startX */, mViewStartY /* startY */, 0 /* velocityX */, - (int) -velocityY, 0 /* minX */, 0 /* maxX */, 0 /* minY */, - mMaxViewStartY /* maxY */, OVERFLING_DISTANCE, OVERFLING_DISTANCE); - - // When flinging down, show a glow when it hits the end only if it - // wasn't started at the top - if (velocityY > 0 && mViewStartY != 0) { - mCallEdgeEffectOnAbsorb = true; - } - // When flinging up, show a glow when it hits the end only if it wasn't - // started at the bottom - else if (velocityY < 0 && mViewStartY != mMaxViewStartY) { - mCallEdgeEffectOnAbsorb = true; - } - mHandler.post(mContinueScroll); - } - - private boolean initNextView(int deltaX) { - // Change the view to the previous day or week - DayView view = (DayView) mViewSwitcher.getNextView(); - Time date = view.mBaseDate; - date.set(mBaseDate); - boolean switchForward; - if (deltaX > 0) { - date.monthDay -= mNumDays; - view.setSelectedDay(mSelectionDay - mNumDays); - switchForward = false; - } else { - date.monthDay += mNumDays; - view.setSelectedDay(mSelectionDay + mNumDays); - switchForward = true; - } - date.normalize(true /* ignore isDst */); - initView(view); - view.layout(getLeft(), getTop(), getRight(), getBottom()); - view.reloadEvents(); - return switchForward; - } - - // ScaleGestureDetector.OnScaleGestureListener - public boolean onScaleBegin(ScaleGestureDetector detector) { - mHandleActionUp = false; - float gestureCenterInPixels = detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight; - mGestureCenterHour = (mViewStartY + gestureCenterInPixels) / (mCellHeight + DAY_GAP); - - mStartingSpanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY())); - mCellHeightBeforeScaleGesture = mCellHeight; - - if (DEBUG_SCALING) { - float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP); - Log.d(TAG, "onScaleBegin: mGestureCenterHour:" + mGestureCenterHour - + "\tViewStartHour: " + ViewStartHour + "\tmViewStartY:" + mViewStartY - + "\tmCellHeight:" + mCellHeight + " SpanY:" + detector.getCurrentSpanY()); - } - - return true; - } - - // ScaleGestureDetector.OnScaleGestureListener - public boolean onScale(ScaleGestureDetector detector) { - float spanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY())); - - mCellHeight = (int) (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY); - - if (mCellHeight < mMinCellHeight) { - // If mStartingSpanY is too small, even a small increase in the - // gesture can bump the mCellHeight beyond MAX_CELL_HEIGHT - mStartingSpanY = spanY; - mCellHeight = mMinCellHeight; - mCellHeightBeforeScaleGesture = mMinCellHeight; - } else if (mCellHeight > MAX_CELL_HEIGHT) { - mStartingSpanY = spanY; - mCellHeight = MAX_CELL_HEIGHT; - mCellHeightBeforeScaleGesture = MAX_CELL_HEIGHT; - } - - int gestureCenterInPixels = (int) detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight; - mViewStartY = (int) (mGestureCenterHour * (mCellHeight + DAY_GAP)) - gestureCenterInPixels; - mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight; - - if (DEBUG_SCALING) { - float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP); - Log.d(TAG, "onScale: mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: " - + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:" - + mCellHeight + " SpanY:" + detector.getCurrentSpanY()); - } - - if (mViewStartY < 0) { - mViewStartY = 0; - mGestureCenterHour = (mViewStartY + gestureCenterInPixels) - / (float) (mCellHeight + DAY_GAP); - } else if (mViewStartY > mMaxViewStartY) { - mViewStartY = mMaxViewStartY; - mGestureCenterHour = (mViewStartY + gestureCenterInPixels) - / (float) (mCellHeight + DAY_GAP); - } - computeFirstHour(); - - mRemeasure = true; - invalidate(); - return true; - } - - // ScaleGestureDetector.OnScaleGestureListener - public void onScaleEnd(ScaleGestureDetector detector) { - mScrollStartY = mViewStartY; - mInitialScrollY = 0; - mInitialScrollX = 0; - mStartingSpanY = 0; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - int action = ev.getAction(); - if (DEBUG) Log.e(TAG, "" + action + " ev.getPointerCount() = " + ev.getPointerCount()); - - if ((ev.getActionMasked() == MotionEvent.ACTION_DOWN) || - (ev.getActionMasked() == MotionEvent.ACTION_UP) || - (ev.getActionMasked() == MotionEvent.ACTION_POINTER_UP) || - (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN)) { - mRecalCenterHour = true; - } - - if ((mTouchMode & TOUCH_MODE_HSCROLL) == 0) { - mScaleGestureDetector.onTouchEvent(ev); - } - - switch (action) { - case MotionEvent.ACTION_DOWN: - mStartingScroll = true; - if (DEBUG) { - Log.e(TAG, "ACTION_DOWN ev.getDownTime = " + ev.getDownTime() + " Cnt=" - + ev.getPointerCount()); - } - - int bottom = mAlldayHeight + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN; - if (ev.getY() < bottom) { - mTouchStartedInAlldayArea = true; - } else { - mTouchStartedInAlldayArea = false; - } - mHandleActionUp = true; - mGestureDetector.onTouchEvent(ev); - return true; - - case MotionEvent.ACTION_MOVE: - if (DEBUG) Log.e(TAG, "ACTION_MOVE Cnt=" + ev.getPointerCount() + DayView.this); - mGestureDetector.onTouchEvent(ev); - return true; - - case MotionEvent.ACTION_UP: - if (DEBUG) Log.e(TAG, "ACTION_UP Cnt=" + ev.getPointerCount() + mHandleActionUp); - mEdgeEffectTop.onRelease(); - mEdgeEffectBottom.onRelease(); - mStartingScroll = false; - mGestureDetector.onTouchEvent(ev); - if (!mHandleActionUp) { - mHandleActionUp = true; - mViewStartX = 0; - invalidate(); - return true; - } - - if (mOnFlingCalled) { - return true; - } - - // If we were scrolling, then reset the selected hour so that it - // is visible. - if (mScrolling) { - mScrolling = false; - resetSelectedHour(); - invalidate(); - } - - if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { - mTouchMode = TOUCH_MODE_INITIAL_STATE; - if (Math.abs(mViewStartX) > mHorizontalSnapBackThreshold) { - // The user has gone beyond the threshold so switch views - if (DEBUG) Log.d(TAG, "- horizontal scroll: switch views"); - switchViews(mViewStartX > 0, mViewStartX, mViewWidth, 0); - mViewStartX = 0; - return true; - } else { - // Not beyond the threshold so invalidate which will cause - // the view to snap back. Also call recalc() to ensure - // that we have the correct starting date and title. - if (DEBUG) Log.d(TAG, "- horizontal scroll: snap back"); - recalc(); - invalidate(); - mViewStartX = 0; - } - } - - return true; - - // This case isn't expected to happen. - case MotionEvent.ACTION_CANCEL: - if (DEBUG) Log.e(TAG, "ACTION_CANCEL"); - mGestureDetector.onTouchEvent(ev); - mScrolling = false; - resetSelectedHour(); - return true; - - default: - if (DEBUG) Log.e(TAG, "Not MotionEvent " + ev.toString()); - if (mGestureDetector.onTouchEvent(ev)) { - return true; - } - return super.onTouchEvent(ev); - } - } - - public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) { - MenuItem item; - - // If the trackball is held down, then the context menu pops up and - // we never get onKeyUp() for the long-press. So check for it here - // and change the selection to the long-press state. - if (mSelectionMode != SELECTION_LONGPRESS) { - invalidate(); - } - - final long startMillis = getSelectedTimeInMillis(); - int flags = DateUtils.FORMAT_SHOW_TIME - | DateUtils.FORMAT_CAP_NOON_MIDNIGHT - | DateUtils.FORMAT_SHOW_WEEKDAY; - final String title = Utils.formatDateRange(mContext, startMillis, startMillis, flags); - menu.setHeaderTitle(title); - - mPopup.dismiss(); - } - - /** - * Sets mSelectionDay and mSelectionHour based on the (x,y) touch position. - * If the touch position is not within the displayed grid, then this - * method returns false. - * - * @param x the x position of the touch - * @param y the y position of the touch - * @param keepOldSelection - do not change the selection info (used for invoking accessibility - * messages) - * @return true if the touch position is valid - */ - private boolean setSelectionFromPosition(int x, final int y, boolean keepOldSelection) { - - Event savedEvent = null; - int savedDay = 0; - int savedHour = 0; - boolean savedAllDay = false; - if (keepOldSelection) { - // Store selection info and restore it at the end. This way, we can invoke the - // right accessibility message without affecting the selection. - savedEvent = mSelectedEvent; - savedDay = mSelectionDay; - savedHour = mSelectionHour; - savedAllDay = mSelectionAllday; - } - if (x < mHoursWidth) { - x = mHoursWidth; - } - - int day = (x - mHoursWidth) / (mCellWidth + DAY_GAP); - if (day >= mNumDays) { - day = mNumDays - 1; - } - day += mFirstJulianDay; - setSelectedDay(day); - - if (y < DAY_HEADER_HEIGHT) { - sendAccessibilityEventAsNeeded(false); - return false; - } - - setSelectedHour(mFirstHour); /* First fully visible hour */ - - if (y < mFirstCell) { - mSelectionAllday = true; - } else { - // y is now offset from top of the scrollable region - int adjustedY = y - mFirstCell; - - if (adjustedY < mFirstHourOffset) { - setSelectedHour(mSelectionHour - 1); /* In the partially visible hour */ - } else { - setSelectedHour(mSelectionHour + - (adjustedY - mFirstHourOffset) / (mCellHeight + HOUR_GAP)); - } - - mSelectionAllday = false; - } - - findSelectedEvent(x, y); - - sendAccessibilityEventAsNeeded(true); - - // Restore old values - if (keepOldSelection) { - mSelectedEvent = savedEvent; - mSelectionDay = savedDay; - mSelectionHour = savedHour; - mSelectionAllday = savedAllDay; - } - return true; - } - - private void findSelectedEvent(int x, int y) { - int date = mSelectionDay; - int cellWidth = mCellWidth; - ArrayList events = mEvents; - int numEvents = events.size(); - int left = computeDayLeftPosition(mSelectionDay - mFirstJulianDay); - int top = 0; - setSelectedEvent(null); - - mSelectedEvents.clear(); - if (mSelectionAllday) { - float yDistance; - float minYdistance = 10000.0f; // any large number - Event closestEvent = null; - float drawHeight = mAlldayHeight; - int yOffset = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN; - int maxUnexpandedColumn = mMaxUnexpandedAlldayEventCount; - if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) { - // Leave a gap for the 'box +n' text - maxUnexpandedColumn--; - } - events = mAllDayEvents; - numEvents = events.size(); - for (int i = 0; i < numEvents; i++) { - Event event = events.get(i); - if (!event.drawAsAllday() || - (!mShowAllAllDayEvents && event.getColumn() >= maxUnexpandedColumn)) { - // Don't check non-allday events or events that aren't shown - continue; - } - - if (event.startDay <= mSelectionDay && event.endDay >= mSelectionDay) { - float numRectangles = mShowAllAllDayEvents ? mMaxAlldayEvents - : mMaxUnexpandedAlldayEventCount; - float height = drawHeight / numRectangles; - if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) { - height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; - } - float eventTop = yOffset + height * event.getColumn(); - float eventBottom = eventTop + height; - if (eventTop < y && eventBottom > y) { - // If the touch is inside the event rectangle, then - // add the event. - mSelectedEvents.add(event); - closestEvent = event; - break; - } else { - // Find the closest event - if (eventTop >= y) { - yDistance = eventTop - y; - } else { - yDistance = y - eventBottom; - } - if (yDistance < minYdistance) { - minYdistance = yDistance; - closestEvent = event; - } - } - } - } - setSelectedEvent(closestEvent); - return; - } - - // Adjust y for the scrollable bitmap - y += mViewStartY - mFirstCell; - - // Use a region around (x,y) for the selection region - Rect region = mRect; - region.left = x - 10; - region.right = x + 10; - region.top = y - 10; - region.bottom = y + 10; - - EventGeometry geometry = mEventGeometry; - - for (int i = 0; i < numEvents; i++) { - Event event = events.get(i); - // Compute the event rectangle. - if (!geometry.computeEventRect(date, left, top, cellWidth, event)) { - continue; - } - - // If the event intersects the selection region, then add it to - // mSelectedEvents. - if (geometry.eventIntersectsSelection(event, region)) { - mSelectedEvents.add(event); - } - } - - // If there are any events in the selected region, then assign the - // closest one to mSelectedEvent. - if (mSelectedEvents.size() > 0) { - int len = mSelectedEvents.size(); - Event closestEvent = null; - float minDist = mViewWidth + mViewHeight; // some large distance - for (int index = 0; index < len; index++) { - Event ev = mSelectedEvents.get(index); - float dist = geometry.pointToEvent(x, y, ev); - if (dist < minDist) { - minDist = dist; - closestEvent = ev; - } - } - setSelectedEvent(closestEvent); - - // Keep the selected hour and day consistent with the selected - // event. They could be different if we touched on an empty hour - // slot very close to an event in the previous hour slot. In - // that case we will select the nearby event. - int startDay = mSelectedEvent.startDay; - int endDay = mSelectedEvent.endDay; - if (mSelectionDay < startDay) { - setSelectedDay(startDay); - } else if (mSelectionDay > endDay) { - setSelectedDay(endDay); - } - - int startHour = mSelectedEvent.startTime / 60; - int endHour; - if (mSelectedEvent.startTime < mSelectedEvent.endTime) { - endHour = (mSelectedEvent.endTime - 1) / 60; - } else { - endHour = mSelectedEvent.endTime / 60; - } - - if (mSelectionHour < startHour && mSelectionDay == startDay) { - setSelectedHour(startHour); - } else if (mSelectionHour > endHour && mSelectionDay == endDay) { - setSelectedHour(endHour); - } - } - } - - // Encapsulates the code to continue the scrolling after the - // finger is lifted. Instead of stopping the scroll immediately, - // the scroll continues to "free spin" and gradually slows down. - private class ContinueScroll implements Runnable { - - public void run() { - mScrolling = mScrolling && mScroller.computeScrollOffset(); - if (!mScrolling || mPaused) { - resetSelectedHour(); - invalidate(); - return; - } - - mViewStartY = mScroller.getCurrY(); - - if (mCallEdgeEffectOnAbsorb) { - if (mViewStartY < 0) { - mEdgeEffectTop.onAbsorb((int) mLastVelocity); - mCallEdgeEffectOnAbsorb = false; - } else if (mViewStartY > mMaxViewStartY) { - mEdgeEffectBottom.onAbsorb((int) mLastVelocity); - mCallEdgeEffectOnAbsorb = false; - } - mLastVelocity = mScroller.getCurrVelocity(); - } - - if (mScrollStartY == 0 || mScrollStartY == mMaxViewStartY) { - // Allow overscroll/springback only on a fling, - // not a pull/fling from the end - if (mViewStartY < 0) { - mViewStartY = 0; - } else if (mViewStartY > mMaxViewStartY) { - mViewStartY = mMaxViewStartY; - } - } - - computeFirstHour(); - mHandler.post(this); - invalidate(); - } - } - - /** - * Cleanup the pop-up and timers. - */ - public void cleanup() { - // Protect against null-pointer exceptions - if (mPopup != null) { - mPopup.dismiss(); - } - mPaused = true; - mLastPopupEventID = INVALID_EVENT_ID; - if (mHandler != null) { - mHandler.removeCallbacks(mDismissPopup); - mHandler.removeCallbacks(mUpdateCurrentTime); - } - - Utils.setSharedPreference(mContext, GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, - mCellHeight); - // Clear all click animations - eventClickCleanup(); - // Turn off redraw - mRemeasure = false; - // Turn off scrolling to make sure the view is in the correct state if we fling back to it - mScrolling = false; - } - - private void eventClickCleanup() { - this.removeCallbacks(mClearClick); - this.removeCallbacks(mSetClick); - mClickedEvent = null; - mSavedClickedEvent = null; - } - - private void setSelectedEvent(Event e) { - mSelectedEvent = e; - mSelectedEventForAccessibility = e; - } - - private void setSelectedHour(int h) { - mSelectionHour = h; - mSelectionHourForAccessibility = h; - } - private void setSelectedDay(int d) { - mSelectionDay = d; - mSelectionDayForAccessibility = d; - } - - /** - * Restart the update timer - */ - public void restartCurrentTimeUpdates() { - mPaused = false; - if (mHandler != null) { - mHandler.removeCallbacks(mUpdateCurrentTime); - mHandler.post(mUpdateCurrentTime); - } - } - - @Override - protected void onDetachedFromWindow() { - cleanup(); - super.onDetachedFromWindow(); - } - - class DismissPopup implements Runnable { - - public void run() { - // Protect against null-pointer exceptions - if (mPopup != null) { - mPopup.dismiss(); - } - } - } - - class UpdateCurrentTime implements Runnable { - - public void run() { - long currentTime = System.currentTimeMillis(); - mCurrentTime.set(currentTime); - //% causes update to occur on 5 minute marks (11:10, 11:15, 11:20, etc.) - if (!DayView.this.mPaused) { - mHandler.postDelayed(mUpdateCurrentTime, UPDATE_CURRENT_TIME_DELAY - - (currentTime % UPDATE_CURRENT_TIME_DELAY)); - } - mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff); - invalidate(); - } - } - - class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { - @Override - public boolean onSingleTapUp(MotionEvent ev) { - if (DEBUG) Log.e(TAG, "GestureDetector.onSingleTapUp"); - DayView.this.doSingleTapUp(ev); - return true; - } - - @Override - public void onLongPress(MotionEvent ev) { - if (DEBUG) Log.e(TAG, "GestureDetector.onLongPress"); - DayView.this.doLongPress(ev); - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (DEBUG) Log.e(TAG, "GestureDetector.onScroll"); - eventClickCleanup(); - if (mTouchStartedInAlldayArea) { - if (Math.abs(distanceX) < Math.abs(distanceY)) { - // Make sure that click feedback is gone when you scroll from the - // all day area - invalidate(); - return false; - } - // don't scroll vertically if this started in the allday area - distanceY = 0; - } - DayView.this.doScroll(e1, e2, distanceX, distanceY); - return true; - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if (DEBUG) Log.e(TAG, "GestureDetector.onFling"); - - if (mTouchStartedInAlldayArea) { - if (Math.abs(velocityX) < Math.abs(velocityY)) { - return false; - } - // don't fling vertically if this started in the allday area - velocityY = 0; - } - DayView.this.doFling(e1, e2, velocityX, velocityY); - return true; - } - - @Override - public boolean onDown(MotionEvent ev) { - if (DEBUG) Log.e(TAG, "GestureDetector.onDown"); - DayView.this.doDown(ev); - return true; - } - } - - @Override - public boolean onLongClick(View v) { - return true; - } - - // The rest of this file was borrowed from Launcher2 - PagedView.java - private static final int MINIMUM_SNAP_VELOCITY = 2200; - - private class ScrollInterpolator implements Interpolator { - public ScrollInterpolator() { - } - - public float getInterpolation(float t) { - t -= 1.0f; - t = t * t * t * t * t + 1; - - if ((1 - t) * mAnimationDistance < 1) { - cancelAnimation(); - } - - return t; - } - } - - private long calculateDuration(float delta, float width, float velocity) { - /* - * Here we compute a "distance" that will be used in the computation of - * the overall snap duration. This is a function of the actual distance - * that needs to be traveled; we keep this value close to half screen - * size in order to reduce the variance in snap duration as a function - * of the distance the page needs to travel. - */ - final float halfScreenSize = width / 2; - float distanceRatio = delta / width; - float distanceInfluenceForSnapDuration = distanceInfluenceForSnapDuration(distanceRatio); - float distance = halfScreenSize + halfScreenSize * distanceInfluenceForSnapDuration; - - velocity = Math.abs(velocity); - velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity); - - /* - * we want the page's snap velocity to approximately match the velocity - * at which the user flings, so we scale the duration by a value near to - * the derivative of the scroll interpolator at zero, ie. 5. We use 6 to - * make it a little slower. - */ - long duration = 6 * Math.round(1000 * Math.abs(distance / velocity)); - if (DEBUG) { - Log.e(TAG, "halfScreenSize:" + halfScreenSize + " delta:" + delta + " distanceRatio:" - + distanceRatio + " distance:" + distance + " velocity:" + velocity - + " duration:" + duration + " distanceInfluenceForSnapDuration:" - + distanceInfluenceForSnapDuration); - } - return duration; - } - - /* - * We want the duration of the page snap animation to be influenced by the - * distance that the screen has to travel, however, we don't want this - * duration to be effected in a purely linear fashion. Instead, we use this - * method to moderate the effect that the distance of travel has on the - * overall snap duration. - */ - private float distanceInfluenceForSnapDuration(float f) { - f -= 0.5f; // center the values about 0. - f *= 0.3f * Math.PI / 2.0f; - return (float) Math.sin(f); - } -} diff --git a/src/com/android/calendar/Event.java b/src/com/android/calendar/Event.java deleted file mode 100644 index 095e43e7..00000000 --- a/src/com/android/calendar/Event.java +++ /dev/null @@ -1,642 +0,0 @@ -/* - * 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.ContentUris; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.os.Debug; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.provider.CalendarContract.Instances; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.concurrent.atomic.AtomicInteger; - -// TODO: should Event be Parcelable so it can be passed via Intents? -public class Event implements Cloneable { - - private static final String TAG = "CalEvent"; - private static final boolean PROFILE = false; - - /** - * The sort order is: - * 1) events with an earlier start (begin for normal events, startday for allday) - * 2) events with a later end (end for normal events, endday for allday) - * 3) the title (unnecessary, but nice) - * - * The start and end day is sorted first so that all day events are - * sorted correctly with respect to events that are >24 hours (and - * therefore show up in the allday area). - */ - private static final String SORT_EVENTS_BY = - "begin ASC, end DESC, title ASC"; - private static final String SORT_ALLDAY_BY = - "startDay ASC, endDay DESC, title ASC"; - private static final String DISPLAY_AS_ALLDAY = "dispAllday"; - - private static final String EVENTS_WHERE = DISPLAY_AS_ALLDAY + "=0"; - private static final String ALLDAY_WHERE = DISPLAY_AS_ALLDAY + "=1"; - - // The projection to use when querying instances to build a list of events - public static final String[] EVENT_PROJECTION = new String[] { - Instances.TITLE, // 0 - Instances.EVENT_LOCATION, // 1 - Instances.ALL_DAY, // 2 - Instances.DISPLAY_COLOR, // 3 If SDK < 16, set to Instances.CALENDAR_COLOR. - Instances.EVENT_TIMEZONE, // 4 - Instances.EVENT_ID, // 5 - Instances.BEGIN, // 6 - Instances.END, // 7 - Instances._ID, // 8 - Instances.START_DAY, // 9 - Instances.END_DAY, // 10 - Instances.START_MINUTE, // 11 - Instances.END_MINUTE, // 12 - Instances.HAS_ALARM, // 13 - Instances.RRULE, // 14 - Instances.RDATE, // 15 - Instances.SELF_ATTENDEE_STATUS, // 16 - Events.ORGANIZER, // 17 - Events.GUESTS_CAN_MODIFY, // 18 - Instances.ALL_DAY + "=1 OR (" + Instances.END + "-" + Instances.BEGIN + ")>=" - + DateUtils.DAY_IN_MILLIS + " AS " + DISPLAY_AS_ALLDAY, // 19 - }; - - // The indices for the projection array above. - private static final int PROJECTION_TITLE_INDEX = 0; - private static final int PROJECTION_LOCATION_INDEX = 1; - private static final int PROJECTION_ALL_DAY_INDEX = 2; - private static final int PROJECTION_COLOR_INDEX = 3; - private static final int PROJECTION_TIMEZONE_INDEX = 4; - private static final int PROJECTION_EVENT_ID_INDEX = 5; - private static final int PROJECTION_BEGIN_INDEX = 6; - private static final int PROJECTION_END_INDEX = 7; - private static final int PROJECTION_START_DAY_INDEX = 9; - private static final int PROJECTION_END_DAY_INDEX = 10; - private static final int PROJECTION_START_MINUTE_INDEX = 11; - private static final int PROJECTION_END_MINUTE_INDEX = 12; - private static final int PROJECTION_HAS_ALARM_INDEX = 13; - 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; - private static final int PROJECTION_DISPLAY_AS_ALLDAY = 19; - - static { - if (!Utils.isJellybeanOrLater()) { - EVENT_PROJECTION[PROJECTION_COLOR_INDEX] = Instances.CALENDAR_COLOR; - } - } - - private static String mNoTitleString; - private static int mNoColorColor; - - 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 - public int startTime; // Start and end time are in minutes since midnight - public int endTime; - - public long startMillis; // UTC milliseconds since the epoch - public long endMillis; // UTC milliseconds since the epoch - private int mColumn; - private int mMaxColumns; - - public boolean hasAlarm; - public boolean isRepeating; - - public int selfAttendeeStatus; - - // The coordinates of the event rectangle drawn on the screen. - public float left; - public float right; - public float top; - public float bottom; - - // These 4 fields are used for navigating among events within the selected - // hour in the Day and Week view. - public Event nextRight; - public Event nextLeft; - public Event nextUp; - public Event nextDown; - - @Override - public final Object clone() throws CloneNotSupportedException { - super.clone(); - Event e = new Event(); - - e.title = title; - e.color = color; - e.location = location; - e.allDay = allDay; - e.startDay = startDay; - e.endDay = endDay; - e.startTime = startTime; - e.endTime = endTime; - e.startMillis = startMillis; - e.endMillis = endMillis; - e.hasAlarm = hasAlarm; - e.isRepeating = isRepeating; - e.selfAttendeeStatus = selfAttendeeStatus; - e.organizer = organizer; - e.guestsCanModify = guestsCanModify; - - return e; - } - - public final void copyTo(Event dest) { - dest.id = id; - dest.title = title; - dest.color = color; - dest.location = location; - dest.allDay = allDay; - dest.startDay = startDay; - dest.endDay = endDay; - dest.startTime = startTime; - dest.endTime = endTime; - dest.startMillis = startMillis; - dest.endMillis = endMillis; - dest.hasAlarm = hasAlarm; - dest.isRepeating = isRepeating; - dest.selfAttendeeStatus = selfAttendeeStatus; - dest.organizer = organizer; - dest.guestsCanModify = guestsCanModify; - } - - public static final Event newInstance() { - Event e = new Event(); - - e.id = 0; - e.title = null; - e.color = 0; - e.location = null; - e.allDay = false; - e.startDay = 0; - e.endDay = 0; - e.startTime = 0; - e.endTime = 0; - e.startMillis = 0; - e.endMillis = 0; - e.hasAlarm = false; - e.isRepeating = false; - e.selfAttendeeStatus = Attendees.ATTENDEE_STATUS_NONE; - - return e; - } - - /** - * Loads days days worth of instances starting at startDay. - */ - public static void loadEvents(Context context, ArrayList events, int startDay, int days, - int requestId, AtomicInteger sequenceNumber) { - - if (PROFILE) { - Debug.startMethodTracing("loadEvents"); - } - - Cursor cEvents = null; - Cursor cAllday = null; - - events.clear(); - try { - int endDay = startDay + days - 1; - - // We use the byDay instances query to get a list of all events for - // the days we're interested in. - // The sort order is: events with an earlier start time occur - // first and if the start times are the same, then events with - // a later end time occur first. The later end time is ordered - // first so that long rectangles in the calendar views appear on - // the left side. If the start and end times of two events are - // the same then we sort alphabetically on the title. This isn't - // required for correctness, it just adds a nice touch. - - // Respect the preference to show/hide declined events - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - boolean hideDeclined = prefs.getBoolean(GeneralPreferences.KEY_HIDE_DECLINED, - false); - - String where = EVENTS_WHERE; - String whereAllday = ALLDAY_WHERE; - if (hideDeclined) { - String hideString = " AND " + Instances.SELF_ATTENDEE_STATUS + "!=" - + Attendees.ATTENDEE_STATUS_DECLINED; - where += hideString; - whereAllday += hideString; - } - - cEvents = instancesQuery(context.getContentResolver(), EVENT_PROJECTION, startDay, - endDay, where, null, SORT_EVENTS_BY); - cAllday = instancesQuery(context.getContentResolver(), EVENT_PROJECTION, startDay, - endDay, whereAllday, null, SORT_ALLDAY_BY); - - // Check if we should return early because there are more recent - // load requests waiting. - if (requestId != sequenceNumber.get()) { - return; - } - - buildEventsFromCursor(events, cEvents, context, startDay, endDay); - buildEventsFromCursor(events, cAllday, context, startDay, endDay); - - } finally { - if (cEvents != null) { - cEvents.close(); - } - if (cAllday != null) { - cAllday.close(); - } - if (PROFILE) { - Debug.stopMethodTracing(); - } - } - } - - /** - * Performs a query to return all visible instances in the given range - * that match the given selection. This is a blocking function and - * should not be done on the UI thread. This will cause an expansion of - * recurring events to fill this time range if they are not already - * expanded and will slow down for larger time ranges with many - * recurring events. - * - * @param cr The ContentResolver to use for the query - * @param projection The columns to return - * @param begin The start of the time range to query in UTC millis since - * epoch - * @param end The end of the time range to query in UTC millis since - * epoch - * @param selection Filter on the query as an SQL WHERE statement - * @param selectionArgs Args to replace any '?'s in the selection - * @param orderBy How to order the rows as an SQL ORDER BY statement - * @return A Cursor of instances matching the selection - */ - private static final Cursor instancesQuery(ContentResolver cr, String[] projection, - int startDay, int endDay, String selection, String[] selectionArgs, String orderBy) { - String WHERE_CALENDARS_SELECTED = Calendars.VISIBLE + "=?"; - String[] WHERE_CALENDARS_ARGS = {"1"}; - String DEFAULT_SORT_ORDER = "begin ASC"; - - Uri.Builder builder = Instances.CONTENT_BY_DAY_URI.buildUpon(); - ContentUris.appendId(builder, startDay); - ContentUris.appendId(builder, endDay); - if (TextUtils.isEmpty(selection)) { - selection = WHERE_CALENDARS_SELECTED; - selectionArgs = WHERE_CALENDARS_ARGS; - } else { - selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED; - if (selectionArgs != null && selectionArgs.length > 0) { - selectionArgs = Arrays.copyOf(selectionArgs, selectionArgs.length + 1); - selectionArgs[selectionArgs.length - 1] = WHERE_CALENDARS_ARGS[0]; - } else { - selectionArgs = WHERE_CALENDARS_ARGS; - } - } - return cr.query(builder.build(), projection, selection, selectionArgs, - orderBy == null ? DEFAULT_SORT_ORDER : orderBy); - } - - /** - * Adds all the events from the cursors to the events list. - * - * @param events The list of events - * @param cEvents Events to add to the list - * @param context - * @param startDay - * @param endDay - */ - public static void buildEventsFromCursor( - ArrayList events, Cursor cEvents, Context context, int startDay, int endDay) { - if (cEvents == null || events == null) { - Log.e(TAG, "buildEventsFromCursor: null cursor or null events list!"); - return; - } - - int count = cEvents.getCount(); - - if (count == 0) { - return; - } - - Resources res = context.getResources(); - mNoTitleString = res.getString(R.string.no_title_label); - mNoColorColor = res.getColor(R.color.event_center); - // Sort events in two passes so we ensure the allday and standard events - // get sorted in the correct order - cEvents.moveToPosition(-1); - while (cEvents.moveToNext()) { - Event e = generateEventFromCursor(cEvents); - if (e.startDay > endDay || e.endDay < startDay) { - continue; - } - events.add(e); - } - } - - /** - * @param cEvents Cursor pointing at event - * @return An event created from the cursor - */ - private static Event generateEventFromCursor(Cursor cEvents) { - Event e = new Event(); - - e.id = cEvents.getLong(PROJECTION_EVENT_ID_INDEX); - e.title = cEvents.getString(PROJECTION_TITLE_INDEX); - e.location = cEvents.getString(PROJECTION_LOCATION_INDEX); - e.allDay = cEvents.getInt(PROJECTION_ALL_DAY_INDEX) != 0; - e.organizer = cEvents.getString(PROJECTION_ORGANIZER_INDEX); - e.guestsCanModify = cEvents.getInt(PROJECTION_GUESTS_CAN_INVITE_OTHERS_INDEX) != 0; - - if (e.title == null || e.title.length() == 0) { - e.title = mNoTitleString; - } - - if (!cEvents.isNull(PROJECTION_COLOR_INDEX)) { - // Read the color from the database - e.color = Utils.getDisplayColorFromColor(cEvents.getInt(PROJECTION_COLOR_INDEX)); - } else { - e.color = mNoColorColor; - } - - long eStart = cEvents.getLong(PROJECTION_BEGIN_INDEX); - long eEnd = cEvents.getLong(PROJECTION_END_INDEX); - - e.startMillis = eStart; - e.startTime = cEvents.getInt(PROJECTION_START_MINUTE_INDEX); - e.startDay = cEvents.getInt(PROJECTION_START_DAY_INDEX); - - e.endMillis = eEnd; - e.endTime = cEvents.getInt(PROJECTION_END_MINUTE_INDEX); - e.endDay = cEvents.getInt(PROJECTION_END_DAY_INDEX); - - e.hasAlarm = cEvents.getInt(PROJECTION_HAS_ALARM_INDEX) != 0; - - // Check if this is a repeating event - String rrule = cEvents.getString(PROJECTION_RRULE_INDEX); - String rdate = cEvents.getString(PROJECTION_RDATE_INDEX); - if (!TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate)) { - e.isRepeating = true; - } else { - e.isRepeating = false; - } - - e.selfAttendeeStatus = cEvents.getInt(PROJECTION_SELF_ATTENDEE_STATUS_INDEX); - return e; - } - - /** - * Computes a position for each event. Each event is displayed - * as a non-overlapping rectangle. For normal events, these rectangles - * are displayed in separate columns in the week view and day view. For - * all-day events, these rectangles are displayed in separate rows along - * the top. In both cases, each event is assigned two numbers: N, and - * Max, that specify that this event is the Nth event of Max number of - * events that are displayed in a group. The width and position of each - * rectangle depend on the maximum number of rectangles that occur at - * the same time. - * - * @param eventsList the list of events, sorted into increasing time order - * @param minimumDurationMillis minimum duration acceptable as cell height of each event - * rectangle in millisecond. Should be 0 when it is not determined. - */ - /* package */ static void computePositions(ArrayList eventsList, - long minimumDurationMillis) { - if (eventsList == null) { - return; - } - - // Compute the column positions separately for the all-day events - doComputePositions(eventsList, minimumDurationMillis, false); - doComputePositions(eventsList, minimumDurationMillis, true); - } - - private static void doComputePositions(ArrayList eventsList, - long minimumDurationMillis, boolean doAlldayEvents) { - final ArrayList activeList = new ArrayList(); - final ArrayList groupList = new ArrayList(); - - if (minimumDurationMillis < 0) { - minimumDurationMillis = 0; - } - - long colMask = 0; - int maxCols = 0; - for (Event event : eventsList) { - // Process all-day events separately - if (event.drawAsAllday() != doAlldayEvents) - continue; - - if (!doAlldayEvents) { - colMask = removeNonAlldayActiveEvents( - event, activeList.iterator(), minimumDurationMillis, colMask); - } else { - colMask = removeAlldayActiveEvents(event, activeList.iterator(), colMask); - } - - // If the active list is empty, then reset the max columns, clear - // the column bit mask, and empty the groupList. - if (activeList.isEmpty()) { - for (Event ev : groupList) { - ev.setMaxColumns(maxCols); - } - maxCols = 0; - colMask = 0; - groupList.clear(); - } - - // Find the first empty column. Empty columns are represented by - // zero bits in the column mask "colMask". - int col = findFirstZeroBit(colMask); - if (col == 64) - col = 63; - colMask |= (1L << col); - event.setColumn(col); - activeList.add(event); - groupList.add(event); - int len = activeList.size(); - if (maxCols < len) - maxCols = len; - } - for (Event ev : groupList) { - ev.setMaxColumns(maxCols); - } - } - - private static long removeAlldayActiveEvents(Event event, Iterator iter, long colMask) { - // Remove the inactive allday events. An event on the active list - // becomes inactive when the end day is less than the current event's - // start day. - while (iter.hasNext()) { - final Event active = iter.next(); - if (active.endDay < event.startDay) { - colMask &= ~(1L << active.getColumn()); - iter.remove(); - } - } - return colMask; - } - - private static long removeNonAlldayActiveEvents( - Event event, Iterator iter, long minDurationMillis, long colMask) { - long start = event.getStartMillis(); - // Remove the inactive events. An event on the active list - // becomes inactive when its end time is less than or equal to - // the current event's start time. - while (iter.hasNext()) { - final Event active = iter.next(); - - final long duration = Math.max( - active.getEndMillis() - active.getStartMillis(), minDurationMillis); - if ((active.getStartMillis() + duration) <= start) { - colMask &= ~(1L << active.getColumn()); - iter.remove(); - } - } - return colMask; - } - - public static int findFirstZeroBit(long val) { - for (int ii = 0; ii < 64; ++ii) { - if ((val & (1L << ii)) == 0) - return ii; - } - return 64; - } - - public final void dump() { - Log.e("Cal", "+-----------------------------------------+"); - Log.e("Cal", "+ id = " + id); - Log.e("Cal", "+ color = " + color); - Log.e("Cal", "+ title = " + title); - Log.e("Cal", "+ location = " + location); - Log.e("Cal", "+ allDay = " + allDay); - Log.e("Cal", "+ startDay = " + startDay); - 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, - int endMinute) { - if (endDay < julianDay) { - return false; - } - - if (startDay > julianDay) { - return false; - } - - if (endDay == julianDay) { - if (endTime < startMinute) { - return false; - } - // An event that ends at the start minute should not be considered - // as intersecting the given time span, but don't exclude - // zero-length (or very short) events. - if (endTime == startMinute - && (startTime != endTime || startDay != endDay)) { - return false; - } - } - - if (startDay == julianDay && startTime > endMinute) { - return false; - } - - return true; - } - - /** - * Returns the event title and location separated by a comma. If the - * location is already part of the title (at the end of the title), then - * just the title is returned. - * - * @return the event title and location as a String - */ - public String getTitleAndLocation() { - String text = title.toString(); - - // Append the location to the title, unless the title ends with the - // location (for example, "meeting in building 42" ends with the - // location). - if (location != null) { - String locationString = location.toString(); - if (!text.endsWith(locationString)) { - text += ", " + locationString; - } - } - return text; - } - - public void setColumn(int column) { - mColumn = column; - } - - public int getColumn() { - return mColumn; - } - - public void setMaxColumns(int maxColumns) { - mMaxColumns = maxColumns; - } - - public int getMaxColumns() { - return mMaxColumns; - } - - public void setStartMillis(long startMillis) { - this.startMillis = startMillis; - } - - public long getStartMillis() { - return startMillis; - } - - public void setEndMillis(long endMillis) { - this.endMillis = endMillis; - } - - public long getEndMillis() { - return endMillis; - } - - public boolean drawAsAllday() { - // Use >= so we'll pick up Exchange allday events - return allDay || endMillis - startMillis >= DateUtils.DAY_IN_MILLIS; - } -} diff --git a/src/com/android/calendar/EventGeometry.java b/src/com/android/calendar/EventGeometry.java deleted file mode 100644 index cdecb49c..00000000 --- a/src/com/android/calendar/EventGeometry.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2008 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.graphics.Rect; - -public class EventGeometry { - // This is the space from the grid line to the event rectangle. - private int mCellMargin = 0; - - private float mMinuteHeight; - - private float mHourGap; - private float mMinEventHeight; - - void setCellMargin(int cellMargin) { - mCellMargin = cellMargin; - } - - public void setHourGap(float gap) { - mHourGap = gap; - } - - public void setMinEventHeight(float height) { - mMinEventHeight = height; - } - - public void setHourHeight(float height) { - mMinuteHeight = height / 60.0f; - } - - // Computes the rectangle coordinates of the given event on the screen. - // Returns true if the rectangle is visible on the screen. - public boolean computeEventRect(int date, int left, int top, int cellWidth, Event event) { - if (event.drawAsAllday()) { - return false; - } - - float cellMinuteHeight = mMinuteHeight; - int startDay = event.startDay; - int endDay = event.endDay; - - if (startDay > date || endDay < date) { - return false; - } - - int startTime = event.startTime; - int endTime = event.endTime; - - // If the event started on a previous day, then show it starting - // at the beginning of this day. - if (startDay < date) { - startTime = 0; - } - - // If the event ends on a future day, then show it extending to - // the end of this day. - if (endDay > date) { - endTime = DayView.MINUTES_PER_DAY; - } - - int col = event.getColumn(); - int maxCols = event.getMaxColumns(); - int startHour = startTime / 60; - int endHour = endTime / 60; - - // If the end point aligns on a cell boundary then count it as - // ending in the previous cell so that we don't cross the border - // between hours. - if (endHour * 60 == endTime) - endHour -= 1; - - event.top = top; - event.top += (int) (startTime * cellMinuteHeight); - event.top += startHour * mHourGap; - - event.bottom = top; - event.bottom += (int) (endTime * cellMinuteHeight); - event.bottom += endHour * mHourGap - 1; - - // Make the rectangle be at least mMinEventHeight pixels high - if (event.bottom < event.top + mMinEventHeight) { - event.bottom = event.top + mMinEventHeight; - } - - float colWidth = (float) (cellWidth - (maxCols + 1) * mCellMargin) / (float) maxCols; - event.left = left + col * (colWidth + mCellMargin); - event.right = event.left + colWidth; - return true; - } - - /** - * Returns true if this event intersects the selection region. - */ - boolean eventIntersectsSelection(Event event, Rect selection) { - if (event.left < selection.right && event.right >= selection.left - && event.top < selection.bottom && event.bottom >= selection.top) { - return true; - } - return false; - } - - /** - * Computes the distance from the given point to the given event. - */ - float pointToEvent(float x, float y, Event event) { - float left = event.left; - float right = event.right; - float top = event.top; - float bottom = event.bottom; - - if (x >= left) { - if (x <= right) { - if (y >= top) { - if (y <= bottom) { - // x,y is inside the event rectangle - return 0f; - } - // x,y is below the event rectangle - return y - bottom; - } - // x,y is above the event rectangle - return top - y; - } - - // x > right - float dx = x - right; - if (y < top) { - // the upper right corner - float dy = top - y; - return (float) Math.sqrt(dx * dx + dy * dy); - } - if (y > bottom) { - // the lower right corner - float dy = y - bottom; - return (float) Math.sqrt(dx * dx + dy * dy); - } - // x,y is to the right of the event rectangle - return dx; - } - // x < left - float dx = left - x; - if (y < top) { - // the upper left corner - float dy = top - y; - return (float) Math.sqrt(dx * dx + dy * dy); - } - if (y > bottom) { - // the lower left corner - float dy = y - bottom; - return (float) Math.sqrt(dx * dx + dy * dy); - } - // x,y is to the left of the event rectangle - return dx; - } -} diff --git a/src/com/android/calendar/EventInfoActivity.java b/src/com/android/calendar/EventInfoActivity.java deleted file mode 100644 index 626e099d..00000000 --- a/src/com/android/calendar/EventInfoActivity.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2010 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 static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; -import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; -import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; - -import android.app.ActionBar; -import android.app.Activity; -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; - -import java.util.ArrayList; -import java.util.List; - -public class EventInfoActivity extends Activity { - private static final String TAG = "EventInfoActivity"; - private EventInfoFragment mInfoFragment; - 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); - - // Get the info needed for the fragment - Intent intent = getIntent(); - int attendeeResponse = 0; - mEventId = -1; - boolean isDialog = false; - - if (icicle != null) { - mEventId = icicle.getLong(EventInfoFragment.BUNDLE_KEY_EVENT_ID); - mStartMillis = icicle.getLong(EventInfoFragment.BUNDLE_KEY_START_MILLIS); - mEndMillis = icicle.getLong(EventInfoFragment.BUNDLE_KEY_END_MILLIS); - attendeeResponse = icicle.getInt(EventInfoFragment.BUNDLE_KEY_ATTENDEE_RESPONSE); - isDialog = icicle.getBoolean(EventInfoFragment.BUNDLE_KEY_IS_DIALOG); - } else if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { - mStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0); - mEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0); - attendeeResponse = intent.getIntExtra(ATTENDEE_STATUS, - Attendees.ATTENDEE_STATUS_NONE); - Uri data = intent.getData(); - if (data != null) { - try { - List pathSegments = data.getPathSegments(); - int size = pathSegments.size(); - if (size > 2 && "EventTime".equals(pathSegments.get(2))) { - // Support non-standard VIEW intent format: - //dat = content://com.android.calendar/events/[id]/EventTime/[start]/[end] - mEventId = Long.parseLong(pathSegments.get(1)); - if (size > 4) { - mStartMillis = Long.parseLong(pathSegments.get(3)); - mEndMillis = Long.parseLong(pathSegments.get(4)); - } - } else { - mEventId = Long.parseLong(data.getLastPathSegment()); - } - } catch (NumberFormatException e) { - if (mEventId == -1) { - // do nothing here , deal with it later - } else if (mStartMillis == 0 || mEndMillis ==0) { - // Parsing failed on the start or end time , make sure the times were not - // pulled from the intent's extras and reset them. - mStartMillis = 0; - mEndMillis = 0; - } - } - } - } - - if (mEventId == -1) { - Log.w(TAG, "No event id"); - Toast.makeText(this, R.string.event_not_found, Toast.LENGTH_SHORT).show(); - finish(); - } - - // If we do not support showing full screen event info in this configuration, - // close the activity and show the event in AllInOne. - Resources res = getResources(); - if (!res.getBoolean(R.bool.agenda_show_event_info_full_screen) - && !res.getBoolean(R.bool.show_event_info_full_screen)) { - CalendarController.getInstance(this) - .launchViewEvent(mEventId, mStartMillis, mEndMillis, attendeeResponse); - finish(); - return; - } - - setContentView(R.layout.simple_frame_layout); - - // Get the fragment if exists - mInfoFragment = (EventInfoFragment) - getFragmentManager().findFragmentById(R.id.main_frame); - - - // Remove the application title - ActionBar bar = getActionBar(); - if (bar != null) { - bar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME); - } - - // Create a new fragment if none exists - if (mInfoFragment == null) { - FragmentManager fragmentManager = getFragmentManager(); - FragmentTransaction ft = fragmentManager.beginTransaction(); - mInfoFragment = new EventInfoFragment(this, mEventId, mStartMillis, mEndMillis, - attendeeResponse, isDialog, (isDialog ? - EventInfoFragment.DIALOG_WINDOW_STYLE : - EventInfoFragment.FULL_WINDOW_STYLE)); - ft.replace(R.id.main_frame, mInfoFragment); - ft.commit(); - } - } - - @Override - protected void onNewIntent(Intent intent) { - // From the Android Dev Guide: "It's important to note that when - // onNewIntent(Intent) is called, the Activity has not been restarted, - // so the getIntent() method will still return the Intent that was first - // received with onCreate(). This is why setIntent(Intent) is called - // inside onNewIntent(Intent) (just in case you call getIntent() at a - // later time)." - setIntent(intent); - } - - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - } - - @Override - protected void onResume() { - super.onResume(); - getContentResolver().registerContentObserver(CalendarContract.Events.CONTENT_URI, - true, mObserver); - } - - @Override - protected void onPause() { - super.onPause(); - getContentResolver().unregisterContentObserver(mObserver); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - } -} diff --git a/src/com/android/calendar/EventInfoFragment.java b/src/com/android/calendar/EventInfoFragment.java deleted file mode 100644 index 0aa83d02..00000000 --- a/src/com/android/calendar/EventInfoFragment.java +++ /dev/null @@ -1,877 +0,0 @@ -/* - * Copyright (C) 2010 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 static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; -import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; -import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; -import static com.android.calendar.CalendarController.EVENT_EDIT_ON_LAUNCH; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.app.Activity; -import android.app.Dialog; -import android.app.DialogFragment; -import android.app.FragmentManager; -import android.app.Service; -import android.content.ActivityNotFoundException; -import android.content.ContentProviderOperation; -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.SharedPreferences; -import android.content.pm.ApplicationInfo; -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; -import android.os.Bundle; -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.ContactsContract; -import android.provider.ContactsContract.CommonDataKinds; -import android.provider.ContactsContract.Intents; -import android.provider.ContactsContract.QuickContact; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.text.format.Time; -import android.text.method.LinkMovementMethod; -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; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnTouchListener; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.RadioButton; -import android.widget.RadioGroup; -import android.widget.RadioGroup.OnCheckedChangeListener; -import android.widget.ScrollView; -import android.widget.TextView; -import android.widget.Toast; - -import com.android.calendar.CalendarController.EventInfo; -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.alerts.QuickResponseActivity; -import com.android.calendarcommon2.DateException; -import com.android.calendarcommon2.Duration; -import com.android.calendarcommon2.EventRecurrence; -import com.android.colorpicker.HsvColorComparator; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -public class EventInfoFragment extends DialogFragment implements OnCheckedChangeListener, - CalendarController.EventHandler, OnClickListener { - - public static final boolean DEBUG = false; - - public static final String TAG = "EventInfoFragment"; - - protected static final String BUNDLE_KEY_EVENT_ID = "key_event_id"; - protected static final String BUNDLE_KEY_START_MILLIS = "key_start_millis"; - protected static final String BUNDLE_KEY_END_MILLIS = "key_end_millis"; - protected static final String BUNDLE_KEY_IS_DIALOG = "key_fragment_is_dialog"; - protected static final String BUNDLE_KEY_DELETE_DIALOG_VISIBLE = "key_delete_dialog_visible"; - protected static final String BUNDLE_KEY_WINDOW_STYLE = "key_window_style"; - protected static final String BUNDLE_KEY_CALENDAR_COLOR = "key_calendar_color"; - protected static final String BUNDLE_KEY_CALENDAR_COLOR_INIT = "key_calendar_color_init"; - protected static final String BUNDLE_KEY_CURRENT_COLOR = "key_current_color"; - protected static final String BUNDLE_KEY_CURRENT_COLOR_KEY = "key_current_color_key"; - protected static final String BUNDLE_KEY_CURRENT_COLOR_INIT = "key_current_color_init"; - protected static final String BUNDLE_KEY_ORIGINAL_COLOR = "key_original_color"; - protected static final String BUNDLE_KEY_ORIGINAL_COLOR_INIT = "key_original_color_init"; - protected static final String BUNDLE_KEY_ATTENDEE_RESPONSE = "key_attendee_response"; - protected static final String BUNDLE_KEY_USER_SET_ATTENDEE_RESPONSE = - "key_user_set_attendee_response"; - protected static final String BUNDLE_KEY_TENTATIVE_USER_RESPONSE = - "key_tentative_user_response"; - protected static final String BUNDLE_KEY_RESPONSE_WHICH_EVENTS = "key_response_which_events"; - protected static final String BUNDLE_KEY_REMINDER_MINUTES = "key_reminder_minutes"; - protected static final String BUNDLE_KEY_REMINDER_METHODS = "key_reminder_methods"; - - - 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. - */ - static final int UPDATE_SINGLE = 0; - static final int UPDATE_ALL = 1; - - // Style of view - public static final int FULL_WINDOW_STYLE = 0; - public static final int DIALOG_WINDOW_STYLE = 1; - - private int mWindowStyle = DIALOG_WINDOW_STYLE; - - // Query tokens for QueryHandler - private static final int TOKEN_QUERY_EVENT = 1 << 0; - private static final int TOKEN_QUERY_CALENDARS = 1 << 1; - private static final int TOKEN_QUERY_ATTENDEES = 1 << 2; - 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_COLORS; - - private int mCurrentQuery = 0; - - private static final String[] EVENT_PROJECTION = new String[] { - Events._ID, // 0 do not remove; used in DeleteEventHelper - Events.TITLE, // 1 do not remove; used in DeleteEventHelper - Events.RRULE, // 2 do not remove; used in DeleteEventHelper - Events.ALL_DAY, // 3 do not remove; used in DeleteEventHelper - Events.CALENDAR_ID, // 4 do not remove; used in DeleteEventHelper - Events.DTSTART, // 5 do not remove; used in DeleteEventHelper - Events._SYNC_ID, // 6 do not remove; used in DeleteEventHelper - Events.EVENT_TIMEZONE, // 7 do not remove; used in DeleteEventHelper - Events.DESCRIPTION, // 8 - Events.EVENT_LOCATION, // 9 - Calendars.CALENDAR_ACCESS_LEVEL, // 10 - 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; - private static final int EVENT_INDEX_RRULE = 2; - private static final int EVENT_INDEX_ALL_DAY = 3; - private static final int EVENT_INDEX_CALENDAR_ID = 4; - private static final int EVENT_INDEX_DTSTART = 5; - private static final int EVENT_INDEX_SYNC_ID = 6; - private static final int EVENT_INDEX_EVENT_TIMEZONE = 7; - 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_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; - - static { - if (!Utils.isJellybeanOrLater()) { - EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_PACKAGE] = Events._ID; // nonessential value - EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_URI] = Events._ID; // nonessential value - } - } - - static final String[] CALENDARS_PROJECTION = new String[] { - Calendars._ID, // 0 - Calendars.CALENDAR_DISPLAY_NAME, // 1 - Calendars.OWNER_ACCOUNT, // 2 - Calendars.CAN_ORGANIZER_RESPOND, // 3 - 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 + "=?"; - - public static final int COLORS_INDEX_COLOR = 1; - public static final int COLORS_INDEX_COLOR_KEY = 2; - - private View mView; - - private Uri mUri; - private long mEventId; - private Cursor mEventCursor; - private Cursor mCalendarsCursor; - - private static float mScale = 0; // Used for supporting different screen densities - - private static int mCustomAppIconSize = 32; - - private long mStartMillis; - private long mEndMillis; - private boolean mAllDay; - - private boolean mOwnerCanRespond; - private String mSyncAccountName; - private String mCalendarOwnerAccount; - private boolean mIsBusyFreeCalendar; - - private int mOriginalAttendeeResponse; - private int mAttendeeResponseFromIntent = Attendees.ATTENDEE_STATUS_NONE; - private int mUserSetResponse = Attendees.ATTENDEE_STATUS_NONE; - private int mWhichEvents = -1; - // Used as the temporary response until the dialog is confirmed. It is also - // able to be used as a state marker for configuration changes. - private int mTentativeUserSetResponse = Attendees.ATTENDEE_STATUS_NONE; - private boolean mHasAlarm; - // Used to prevent saving changes in event if it is being deleted. - private boolean mEventDeletionStarted = false; - - private TextView mTitle; - private TextView mWhenDateTime; - private TextView mWhere; - private Menu mMenu = null; - private View mHeadlines; - private ScrollView mScrollView; - private View mLoadingMsgView; - private View mErrorMsgView; - private ObjectAnimator mAnimateAlpha; - private long mLoadingMsgStartTime; - - private SparseIntArray mDisplayColorKeyMap = new SparseIntArray(); - private int mOriginalColor = -1; - private boolean mOriginalColorInitialized = false; - private int mCalendarColor = -1; - private boolean mCalendarColorInitialized = false; - private int mCurrentColor = -1; - private boolean mCurrentColorInitialized = false; - private int mCurrentColorKey = -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 - private RadioGroup mResponseRadioGroup; - - ArrayList mToEmails = new ArrayList(); - ArrayList mCcEmails = new ArrayList(); - - - private final Runnable mTZUpdater = new Runnable() { - @Override - public void run() { - updateEvent(mView); - } - }; - - private final Runnable mLoadingMsgAlphaUpdater = new Runnable() { - @Override - public void run() { - // Since this is run after a delay, make sure to only show the message - // if the event's data is not shown yet. - if (!mAnimateAlpha.isRunning() && mScrollView.getAlpha() == 0) { - mLoadingMsgStartTime = System.currentTimeMillis(); - mLoadingMsgView.setAlpha(1); - } - } - }; - - private static int mDialogWidth = 500; - private static int mDialogHeight = 600; - private static int DIALOG_TOP_MARGIN = 8; - private boolean mIsDialog = false; - private boolean mIsPaused = true; - private boolean mDismissOnResume = false; - private int mX = -1; - private int mY = -1; - private int mMinTop; // Dialog cannot be above this location - private boolean mIsTabletConfig; - private Activity mActivity; - private Context mContext; - - private CalendarController mController; - - private void sendAccessibilityEventIfQueryDone(int token) { - mCurrentQuery |= token; - if (mCurrentQuery == TOKEN_QUERY_ALL) { - sendAccessibilityEvent(); - } - } - - public EventInfoFragment(Context context, Uri uri, long startMillis, long endMillis, - int attendeeResponse, boolean isDialog, int windowStyle) { - - Resources r = context.getResources(); - if (mScale == 0) { - mScale = context.getResources().getDisplayMetrics().density; - if (mScale != 1) { - mCustomAppIconSize *= mScale; - if (isDialog) { - DIALOG_TOP_MARGIN *= mScale; - } - } - } - if (isDialog) { - setDialogSize(r); - } - mIsDialog = isDialog; - - setStyle(DialogFragment.STYLE_NO_TITLE, 0); - mUri = uri; - mStartMillis = startMillis; - mEndMillis = endMillis; - mAttendeeResponseFromIntent = attendeeResponse; - mWindowStyle = windowStyle; - } - - // This is currently required by the fragment manager. - 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, - endMillis, attendeeResponse, isDialog, windowStyle); - mEventId = eventId; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - if (mIsDialog) { - applyDialogParams(); - } - - final Activity activity = getActivity(); - mContext = activity; - } - - private void applyDialogParams() { - Dialog dialog = getDialog(); - dialog.setCanceledOnTouchOutside(true); - - Window window = dialog.getWindow(); - window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); - - WindowManager.LayoutParams a = window.getAttributes(); - a.dimAmount = .4f; - - a.width = mDialogWidth; - a.height = mDialogHeight; - - - // On tablets , do smart positioning of dialog - // On phones , use the whole screen - - if (mX != -1 || mY != -1) { - a.x = mX - mDialogWidth / 2; - a.y = mY - mDialogHeight / 2; - if (a.y < mMinTop) { - a.y = mMinTop + DIALOG_TOP_MARGIN; - } - a.gravity = Gravity.LEFT | Gravity.TOP; - } - window.setAttributes(a); - } - - public void setDialogParams(int x, int y, int minTop) { - mX = x; - mY = y; - mMinTop = minTop; - } - - // Implements OnCheckedChangeListener - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - } - - public void onNothingSelected(AdapterView parent) { - } - - @Override - public void onDetach() { - super.onDetach(); - mController.deregisterEventHandler(R.layout.event_info); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mActivity = activity; - // Ensure that mIsTabletConfig is set before creating the menu. - mIsTabletConfig = Utils.getConfigBool(mActivity, R.bool.tablet_config); - mController = CalendarController.getInstance(mActivity); - mController.registerEventHandler(R.layout.event_info, this); - - if (!mIsDialog) { - setHasOptionsMenu(true); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - if (mWindowStyle == DIALOG_WINDOW_STYLE) { - mView = inflater.inflate(R.layout.event_info_dialog, container, false); - } else { - mView = inflater.inflate(R.layout.event_info, container, false); - } - mScrollView = (ScrollView) mView.findViewById(R.id.event_info_scroll_view); - mLoadingMsgView = mView.findViewById(R.id.event_info_loading_msg); - mErrorMsgView = mView.findViewById(R.id.event_info_error_msg); - mTitle = (TextView) mView.findViewById(R.id.title); - mWhenDateTime = (TextView) mView.findViewById(R.id.when_datetime); - mWhere = (TextView) mView.findViewById(R.id.where); - mHeadlines = mView.findViewById(R.id.event_info_headline); - - mResponseRadioGroup = (RadioGroup) mView.findViewById(R.id.response_value); - - mAnimateAlpha = ObjectAnimator.ofFloat(mScrollView, "Alpha", 0, 1); - mAnimateAlpha.setDuration(FADE_IN_TIME); - mAnimateAlpha.addListener(new AnimatorListenerAdapter() { - int defLayerType; - - @Override - public void onAnimationStart(Animator animation) { - // Use hardware layer for better performance during animation - defLayerType = mScrollView.getLayerType(); - mScrollView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - // Ensure that the loading message is gone before showing the - // event info - mLoadingMsgView.removeCallbacks(mLoadingMsgAlphaUpdater); - mLoadingMsgView.setVisibility(View.GONE); - } - - @Override - public void onAnimationCancel(Animator animation) { - mScrollView.setLayerType(defLayerType, null); - } - - @Override - public void onAnimationEnd(Animator animation) { - mScrollView.setLayerType(defLayerType, null); - // Do not cross fade after the first time - mNoCrossFade = true; - } - }); - - mLoadingMsgView.setAlpha(0); - mScrollView.setAlpha(0); - mErrorMsgView.setVisibility(View.INVISIBLE); - mLoadingMsgView.postDelayed(mLoadingMsgAlphaUpdater, LOADING_MSG_DELAY); - - // 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); - } - - return mView; - } - - private void updateTitle() { - Resources res = getActivity().getResources(); - getActivity().setTitle(res.getString(R.string.event_info_title)); - } - - /** - * Initializes the event cursor, which is expected to point to the first - * (and only) result from a query. - * @return false if the cursor is empty, true otherwise - */ - private boolean initEventCursor() { - if ((mEventCursor == null) || (mEventCursor.getCount() == 0)) { - return false; - } - mEventCursor.moveToFirst(); - mEventId = mEventCursor.getInt(EVENT_INDEX_ID); - String rRule = mEventCursor.getString(EVENT_INDEX_RRULE); - // mHasAlarm will be true if it was saved in the event already. - mHasAlarm = (mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) == 1)? true : false; - return true; - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - // Show color/edit/delete buttons only in non-dialog configuration - if (!mIsDialog && !mIsTabletConfig || mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) { - inflater.inflate(R.menu.event_info_title_bar, menu); - mMenu = menu; - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - // If we're a dialog we don't want to handle menu buttons - if (mIsDialog) { - return false; - } - // 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 - - final int itemId = item.getItemId(); - if (itemId == android.R.id.home) { - Utils.returnToCalendarHome(mContext); - mActivity.finish(); - return true; - } else if (itemId == R.id.info_action_edit) { - mActivity.finish(); - } - return super.onOptionsItemSelected(item); - } - - @Override - public void onStop() { - super.onStop(); - } - - @Override - public void onDestroy() { - if (mEventCursor != null) { - mEventCursor.close(); - } - if (mCalendarsCursor != null) { - mCalendarsCursor.close(); - } - super.onDestroy(); - } - - /** - * Creates an exception to a recurring event. The only change we're making is to the - * "self attendee status" value. The provider will take care of updating the corresponding - * Attendees.attendeeStatus entry. - * - * @param eventId The recurring event. - * @param status The new value for selfAttendeeStatus. - */ - private void createExceptionResponse(long eventId, int status) { - ContentValues values = new ContentValues(); - values.put(Events.ORIGINAL_INSTANCE_TIME, mStartMillis); - values.put(Events.SELF_ATTENDEE_STATUS, status); - values.put(Events.STATUS, Events.STATUS_CONFIRMED); - - ArrayList ops = new ArrayList(); - Uri exceptionUri = Uri.withAppendedPath(Events.CONTENT_EXCEPTION_URI, - String.valueOf(eventId)); - ops.add(ContentProviderOperation.newInsert(exceptionUri).withValues(values).build()); - } - - public static int getResponseFromButtonId(int buttonId) { - return Attendees.ATTENDEE_STATUS_NONE; - } - - public static int findButtonIdForResponse(int response) { - return -1; - } - - private void displayEventNotFound() { - mErrorMsgView.setVisibility(View.VISIBLE); - mScrollView.setVisibility(View.GONE); - mLoadingMsgView.setVisibility(View.GONE); - } - - private void updateEvent(View view) { - if (mEventCursor == null || view == null) { - return; - } - - Context context = view.getContext(); - if (context == null) { - return; - } - - String eventName = mEventCursor.getString(EVENT_INDEX_TITLE); - if (eventName == null || eventName.length() == 0) { - eventName = getActivity().getString(R.string.no_title_label); - } - - // 3rd parties might not have specified the start/end time when firing the - // Events.CONTENT_URI intent. Update these with values read from the db. - if (mStartMillis == 0 && mEndMillis == 0) { - mStartMillis = mEventCursor.getLong(EVENT_INDEX_DTSTART); - mEndMillis = mEventCursor.getLong(EVENT_INDEX_DTEND); - if (mEndMillis == 0) { - String duration = mEventCursor.getString(EVENT_INDEX_DURATION); - if (!TextUtils.isEmpty(duration)) { - try { - Duration d = new Duration(); - d.parse(duration); - long endMillis = mStartMillis + d.getMillis(); - if (endMillis >= mStartMillis) { - mEndMillis = endMillis; - } else { - Log.d(TAG, "Invalid duration string: " + duration); - } - } catch (DateException e) { - Log.d(TAG, "Error parsing duration string " + duration, e); - } - } - if (mEndMillis == 0) { - mEndMillis = mStartMillis; - } - } - } - - mAllDay = mEventCursor.getInt(EVENT_INDEX_ALL_DAY) != 0; - String location = mEventCursor.getString(EVENT_INDEX_EVENT_LOCATION); - String description = mEventCursor.getString(EVENT_INDEX_DESCRIPTION); - String rRule = mEventCursor.getString(EVENT_INDEX_RRULE); - String eventTimezone = mEventCursor.getString(EVENT_INDEX_EVENT_TIMEZONE); - - mHeadlines.setBackgroundColor(mCurrentColor); - - // What - if (eventName != null) { - setTextCommon(view, R.id.title, eventName); - } - - // When - // Set the date and repeats (if any) - String localTimezone = Utils.getTimeZone(mActivity, mTZUpdater); - - Resources resources = context.getResources(); - String displayedDatetime = Utils.getDisplayedDatetime(mStartMillis, mEndMillis, - System.currentTimeMillis(), localTimezone, mAllDay, context); - - String displayedTimezone = null; - if (!mAllDay) { - displayedTimezone = Utils.getDisplayedTimezone(mStartMillis, localTimezone, - eventTimezone); - } - // Display the datetime. Make the timezone (if any) transparent. - if (displayedTimezone == null) { - setTextCommon(view, R.id.when_datetime, displayedDatetime); - } else { - int timezoneIndex = displayedDatetime.length(); - displayedDatetime += " " + displayedTimezone; - SpannableStringBuilder sb = new SpannableStringBuilder(displayedDatetime); - ForegroundColorSpan transparentColorSpan = new ForegroundColorSpan( - resources.getColor(R.color.event_info_headline_transparent_color)); - sb.setSpan(transparentColorSpan, timezoneIndex, displayedDatetime.length(), - Spannable.SPAN_INCLUSIVE_INCLUSIVE); - setTextCommon(view, R.id.when_datetime, sb); - } - - view.findViewById(R.id.when_repeat).setVisibility(View.GONE); - - // Organizer view is setup in the updateCalendar method - - - // Where - if (location == null || location.trim().length() == 0) { - setVisibilityCommon(view, R.id.where, View.GONE); - } else { - final TextView textView = mWhere; - if (textView != null) { - textView.setText(location.trim()); - } - } - - // Launch Custom App - if (Utils.isJellybeanOrLater()) { - updateCustomAppButton(); - } - } - - private void updateCustomAppButton() { - setVisibilityCommon(mView, R.id.launch_custom_app_container, View.GONE); - return; - } - - private void sendAccessibilityEvent() { - AccessibilityManager am = - (AccessibilityManager) getActivity().getSystemService(Service.ACCESSIBILITY_SERVICE); - if (!am.isEnabled()) { - return; - } - - AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED); - event.setClassName(EventInfoFragment.class.getName()); - event.setPackageName(getActivity().getPackageName()); - List text = event.getText(); - - if (mResponseRadioGroup.getVisibility() == View.VISIBLE) { - int id = mResponseRadioGroup.getCheckedRadioButtonId(); - if (id != View.NO_ID) { - text.add(((TextView) getView().findViewById(R.id.response_label)).getText()); - text.add((((RadioButton) (mResponseRadioGroup.findViewById(id))) - .getText() + PERIOD_SPACE)); - } - } - - am.sendAccessibilityEvent(event); - } - - private void updateCalendar(View view) { - - mCalendarOwnerAccount = ""; - if (mCalendarsCursor != null && mEventCursor != null) { - mCalendarsCursor.moveToFirst(); - String tempAccount = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT); - mCalendarOwnerAccount = (tempAccount == null) ? "" : tempAccount; - mOwnerCanRespond = mCalendarsCursor.getInt(CALENDARS_INDEX_OWNER_CAN_RESPOND) != 0; - mSyncAccountName = mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_NAME); - - setVisibilityCommon(view, R.id.organizer_container, View.GONE); - mIsBusyFreeCalendar = - mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) == Calendars.CAL_ACCESS_FREEBUSY; - - if (!mIsBusyFreeCalendar) { - - View b = mView.findViewById(R.id.edit); - b.setEnabled(true); - b.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - // For dialogs, just close the fragment - // For full screen, close activity on phone, leave it for tablet - if (mIsDialog) { - EventInfoFragment.this.dismiss(); - } - else if (!mIsTabletConfig){ - getActivity().finish(); - } - } - }); - } - View button; - if ((!mIsDialog && !mIsTabletConfig || - mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) && mMenu != null) { - mActivity.invalidateOptionsMenu(); - } - } else { - setVisibilityCommon(view, R.id.calendar, View.GONE); - sendAccessibilityEventIfQueryDone(TOKEN_QUERY_DUPLICATE_CALENDARS); - } - } - - private void setTextCommon(View view, int id, CharSequence text) { - TextView textView = (TextView) view.findViewById(id); - if (textView == null) - return; - textView.setText(text); - } - - private void setVisibilityCommon(View view, int id, int visibility) { - View v = view.findViewById(id); - if (v != null) { - v.setVisibility(visibility); - } - return; - } - - @Override - public void onPause() { - mIsPaused = true; - super.onPause(); - } - - @Override - public void onResume() { - super.onResume(); - if (mIsDialog) { - setDialogSize(getActivity().getResources()); - applyDialogParams(); - } - mIsPaused = false; - if (mTentativeUserSetResponse != Attendees.ATTENDEE_STATUS_NONE) { - int buttonId = findButtonIdForResponse(mTentativeUserSetResponse); - mResponseRadioGroup.check(buttonId); - } - } - - @Override - public void eventsChanged() { - } - - @Override - public long getSupportedEventTypes() { - return EventType.EVENTS_CHANGED; - } - - @Override - public void handleEvent(EventInfo event) { - reloadEvents(); - } - - public void reloadEvents() { - } - - @Override - public void onClick(View view) { - } - - public long getEventId() { - return mEventId; - } - - public long getStartMillis() { - return mStartMillis; - } - public long getEndMillis() { - return mEndMillis; - } - private void setDialogSize(Resources r) { - 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/EventLoader.java b/src/com/android/calendar/EventLoader.java deleted file mode 100644 index d34b1c7c..00000000 --- a/src/com/android/calendar/EventLoader.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) 2008 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.os.Handler; -import android.os.Process; -import android.provider.CalendarContract; -import android.provider.CalendarContract.EventDays; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; - -public class EventLoader { - - private Context mContext; - private Handler mHandler = new Handler(); - private AtomicInteger mSequenceNumber = new AtomicInteger(); - - private LinkedBlockingQueue mLoaderQueue; - private LoaderThread mLoaderThread; - private ContentResolver mResolver; - - private static interface LoadRequest { - public void processRequest(EventLoader eventLoader); - public void skipRequest(EventLoader eventLoader); - } - - private static class ShutdownRequest implements LoadRequest { - public void processRequest(EventLoader eventLoader) { - } - - public void skipRequest(EventLoader eventLoader) { - } - } - - /** - * - * Code for handling requests to get whether days have an event or not - * and filling in the eventDays array. - * - */ - private static class LoadEventDaysRequest implements LoadRequest { - public int startDay; - public int numDays; - public boolean[] eventDays; - public Runnable uiCallback; - - /** - * The projection used by the EventDays query. - */ - private static final String[] PROJECTION = { - CalendarContract.EventDays.STARTDAY, CalendarContract.EventDays.ENDDAY - }; - - public LoadEventDaysRequest(int startDay, int numDays, boolean[] eventDays, - final Runnable uiCallback) - { - this.startDay = startDay; - this.numDays = numDays; - this.eventDays = eventDays; - this.uiCallback = uiCallback; - } - - @Override - public void processRequest(EventLoader eventLoader) - { - final Handler handler = eventLoader.mHandler; - ContentResolver cr = eventLoader.mResolver; - - // Clear the event days - Arrays.fill(eventDays, false); - - //query which days have events - Cursor cursor = EventDays.query(cr, startDay, numDays, PROJECTION); - try { - int startDayColumnIndex = cursor.getColumnIndexOrThrow(EventDays.STARTDAY); - int endDayColumnIndex = cursor.getColumnIndexOrThrow(EventDays.ENDDAY); - - //Set all the days with events to true - while (cursor.moveToNext()) { - int firstDay = cursor.getInt(startDayColumnIndex); - int lastDay = cursor.getInt(endDayColumnIndex); - //we want the entire range the event occurs, but only within the month - int firstIndex = Math.max(firstDay - startDay, 0); - int lastIndex = Math.min(lastDay - startDay, 30); - - for(int i = firstIndex; i <= lastIndex; i++) { - eventDays[i] = true; - } - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - handler.post(uiCallback); - } - - @Override - public void skipRequest(EventLoader eventLoader) { - } - } - - private static class LoadEventsRequest implements LoadRequest { - - public int id; - public int startDay; - public int numDays; - public ArrayList events; - public Runnable successCallback; - public Runnable cancelCallback; - - public LoadEventsRequest(int id, int startDay, int numDays, ArrayList events, - final Runnable successCallback, final Runnable cancelCallback) { - this.id = id; - this.startDay = startDay; - this.numDays = numDays; - this.events = events; - this.successCallback = successCallback; - this.cancelCallback = cancelCallback; - } - - public void processRequest(EventLoader eventLoader) { - Event.loadEvents(eventLoader.mContext, events, startDay, - numDays, id, eventLoader.mSequenceNumber); - - // Check if we are still the most recent request. - if (id == eventLoader.mSequenceNumber.get()) { - eventLoader.mHandler.post(successCallback); - } else { - eventLoader.mHandler.post(cancelCallback); - } - } - - public void skipRequest(EventLoader eventLoader) { - eventLoader.mHandler.post(cancelCallback); - } - } - - private static class LoaderThread extends Thread { - LinkedBlockingQueue mQueue; - EventLoader mEventLoader; - - public LoaderThread(LinkedBlockingQueue queue, EventLoader eventLoader) { - mQueue = queue; - mEventLoader = eventLoader; - } - - public void shutdown() { - try { - mQueue.put(new ShutdownRequest()); - } catch (InterruptedException ex) { - // The put() method fails with InterruptedException if the - // queue is full. This should never happen because the queue - // has no limit. - Log.e("Cal", "LoaderThread.shutdown() interrupted!"); - } - } - - @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - while (true) { - try { - // Wait for the next request - LoadRequest request = mQueue.take(); - - // If there are a bunch of requests already waiting, then - // skip all but the most recent request. - while (!mQueue.isEmpty()) { - // Let the request know that it was skipped - request.skipRequest(mEventLoader); - - // Skip to the next request - request = mQueue.take(); - } - - if (request instanceof ShutdownRequest) { - return; - } - request.processRequest(mEventLoader); - } catch (InterruptedException ex) { - Log.e("Cal", "background LoaderThread interrupted!"); - } - } - } - } - - public EventLoader(Context context) { - mContext = context; - mLoaderQueue = new LinkedBlockingQueue(); - mResolver = context.getContentResolver(); - } - - /** - * Call this from the activity's onResume() - */ - public void startBackgroundThread() { - mLoaderThread = new LoaderThread(mLoaderQueue, this); - mLoaderThread.start(); - } - - /** - * Call this from the activity's onPause() - */ - public void stopBackgroundThread() { - mLoaderThread.shutdown(); - } - - /** - * Loads "numDays" days worth of events, starting at start, into events. - * Posts uiCallback to the {@link Handler} for this view, which will run in the UI thread. - * Reuses an existing background thread, if events were already being loaded in the background. - * NOTE: events and uiCallback are not used if an existing background thread gets reused -- - * the ones that were passed in on the call that results in the background thread getting - * created are used, and the most recent call's worth of data is loaded into events and posted - * via the uiCallback. - */ - public void loadEventsInBackground(final int numDays, final ArrayList events, - int startDay, final Runnable successCallback, final Runnable cancelCallback) { - - // Increment the sequence number for requests. We don't care if the - // sequence numbers wrap around because we test for equality with the - // latest one. - int id = mSequenceNumber.incrementAndGet(); - - // Send the load request to the background thread - LoadEventsRequest request = new LoadEventsRequest(id, startDay, numDays, - events, successCallback, cancelCallback); - - try { - mLoaderQueue.put(request); - } catch (InterruptedException ex) { - // The put() method fails with InterruptedException if the - // queue is full. This should never happen because the queue - // has no limit. - Log.e("Cal", "loadEventsInBackground() interrupted!"); - } - } - - /** - * Sends a request for the days with events to be marked. Loads "numDays" - * worth of days, starting at start, and fills in eventDays to express which - * days have events. - * - * @param startDay First day to check for events - * @param numDays Days following the start day to check - * @param eventDay Whether or not an event exists on that day - * @param uiCallback What to do when done (log data, redraw screen) - */ - void loadEventDaysInBackground(int startDay, int numDays, boolean[] eventDays, - final Runnable uiCallback) - { - // Send load request to the background thread - LoadEventDaysRequest request = new LoadEventDaysRequest(startDay, numDays, - eventDays, uiCallback); - try { - mLoaderQueue.put(request); - } catch (InterruptedException ex) { - // The put() method fails with InterruptedException if the - // queue is full. This should never happen because the queue - // has no limit. - Log.e("Cal", "loadEventDaysInBackground() interrupted!"); - } - } -} diff --git a/src/com/android/calendar/GeneralPreferences.java b/src/com/android/calendar/GeneralPreferences.java deleted file mode 100644 index a42f07e3..00000000 --- a/src/com/android/calendar/GeneralPreferences.java +++ /dev/null @@ -1,400 +0,0 @@ -/* - * 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.app.Activity; -import android.app.FragmentManager; -import android.app.backup.BackupManager; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.media.Ringtone; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.Vibrator; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceCategory; -import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.preference.RingtonePreference; -import android.provider.CalendarContract; -import android.provider.CalendarContract.CalendarCache; -import android.provider.SearchRecentSuggestions; -import android.text.TextUtils; -import android.text.format.Time; -import android.widget.Toast; - -import com.android.calendar.alerts.AlertReceiver; -import com.android.timezonepicker.TimeZoneInfo; -import com.android.timezonepicker.TimeZonePickerDialog; -import com.android.timezonepicker.TimeZonePickerDialog.OnTimeZoneSetListener; -import com.android.timezonepicker.TimeZonePickerUtils; - -public class GeneralPreferences extends PreferenceFragment implements - OnSharedPreferenceChangeListener, OnPreferenceChangeListener, OnTimeZoneSetListener { - // The name of the shared preferences file. This name must be maintained for historical - // reasons, as it's what PreferenceManager assigned the first time the file was created. - static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; - static final String SHARED_PREFS_NAME_NO_BACKUP = "com.android.calendar_preferences_no_backup"; - - private static final String FRAG_TAG_TIME_ZONE_PICKER = "TimeZonePicker"; - - // Preference keys - public static final String KEY_HIDE_DECLINED = "preferences_hide_declined"; - public static final String KEY_WEEK_START_DAY = "preferences_week_start_day"; - public static final String KEY_SHOW_WEEK_NUM = "preferences_show_week_num"; - public static final String KEY_DAYS_PER_WEEK = "preferences_days_per_week"; - public static final String KEY_SKIP_SETUP = "preferences_skip_setup"; - - public static final String KEY_CLEAR_SEARCH_HISTORY = "preferences_clear_search_history"; - - public static final String KEY_ALERTS_CATEGORY = "preferences_alerts_category"; - public static final String KEY_ALERTS = "preferences_alerts"; - public static final String KEY_ALERTS_VIBRATE = "preferences_alerts_vibrate"; - public static final String KEY_ALERTS_RINGTONE = "preferences_alerts_ringtone"; - public static final String KEY_ALERTS_POPUP = "preferences_alerts_popup"; - - public static final String KEY_SHOW_CONTROLS = "preferences_show_controls"; - - public static final String KEY_DEFAULT_REMINDER = "preferences_default_reminder"; - public static final int NO_REMINDER = -1; - public static final String NO_REMINDER_STRING = "-1"; - public static final int REMINDER_DEFAULT_TIME = 10; // in minutes - - public static final String KEY_DEFAULT_CELL_HEIGHT = "preferences_default_cell_height"; - public static final String KEY_VERSION = "preferences_version"; - - /** Key to SharePreference for default view (CalendarController.ViewType) */ - public static final String KEY_START_VIEW = "preferred_startView"; - /** - * Key to SharePreference for default detail view (CalendarController.ViewType) - * Typically used by widget - */ - public static final String KEY_DETAILED_VIEW = "preferred_detailedView"; - public static final String KEY_DEFAULT_CALENDAR = "preference_defaultCalendar"; - - // These must be in sync with the array preferences_week_start_day_values - public static final String WEEK_START_DEFAULT = "-1"; - public static final String WEEK_START_SATURDAY = "7"; - public static final String WEEK_START_SUNDAY = "1"; - public static final String WEEK_START_MONDAY = "2"; - - // These keys are kept to enable migrating users from previous versions - private static final String KEY_ALERTS_TYPE = "preferences_alerts_type"; - private static final String ALERT_TYPE_ALERTS = "0"; - private static final String ALERT_TYPE_STATUS_BAR = "1"; - private static final String ALERT_TYPE_OFF = "2"; - static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled"; - static final String KEY_HOME_TZ = "preferences_home_tz"; - - // Default preference values - public static final int DEFAULT_START_VIEW = CalendarController.ViewType.WEEK; - public static final int DEFAULT_DETAILED_VIEW = CalendarController.ViewType.DAY; - public static final boolean DEFAULT_SHOW_WEEK_NUM = false; - // This should match the XML file. - public static final String DEFAULT_RINGTONE = "content://settings/system/notification_sound"; - - CheckBoxPreference mAlert; - CheckBoxPreference mVibrate; - CheckBoxPreference mPopup; - CheckBoxPreference mUseHomeTZ; - CheckBoxPreference mHideDeclined; - Preference mHomeTZ; - TimeZonePickerUtils mTzPickerUtils; - ListPreference mWeekStart; - ListPreference mDefaultReminder; - - private String mTimeZoneId; - - /** Return a properly configured SharedPreferences instance */ - public static SharedPreferences getSharedPreferences(Context context) { - return context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - } - - /** Set the default shared preferences in the proper context */ - public static void setDefaultValues(Context context) { - PreferenceManager.setDefaultValues(context, SHARED_PREFS_NAME, Context.MODE_PRIVATE, - R.xml.general_preferences, false); - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - final Activity activity = getActivity(); - - // Make sure to always use the same preferences file regardless of the package name - // we're running under - final PreferenceManager preferenceManager = getPreferenceManager(); - final SharedPreferences sharedPreferences = getSharedPreferences(activity); - preferenceManager.setSharedPreferencesName(SHARED_PREFS_NAME); - - // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.general_preferences); - - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - mAlert = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS); - mVibrate = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS_VIBRATE); - Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); - if (vibrator == null || !vibrator.hasVibrator()) { - PreferenceCategory mAlertGroup = (PreferenceCategory) preferenceScreen - .findPreference(KEY_ALERTS_CATEGORY); - mAlertGroup.removePreference(mVibrate); - } - - mPopup = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS_POPUP); - mUseHomeTZ = (CheckBoxPreference) preferenceScreen.findPreference(KEY_HOME_TZ_ENABLED); - mHideDeclined = (CheckBoxPreference) preferenceScreen.findPreference(KEY_HIDE_DECLINED); - mWeekStart = (ListPreference) preferenceScreen.findPreference(KEY_WEEK_START_DAY); - mDefaultReminder = (ListPreference) preferenceScreen.findPreference(KEY_DEFAULT_REMINDER); - mHomeTZ = preferenceScreen.findPreference(KEY_HOME_TZ); - mWeekStart.setSummary(mWeekStart.getEntry()); - mDefaultReminder.setSummary(mDefaultReminder.getEntry()); - - // This triggers an asynchronous call to the provider to refresh the data in shared pref - mTimeZoneId = Utils.getTimeZone(activity, null); - - SharedPreferences prefs = CalendarUtils.getSharedPreferences(activity, - Utils.SHARED_PREFS_NAME); - - // Utils.getTimeZone will return the currentTimeZone instead of the one - // in the shared_pref if home time zone is disabled. So if home tz is - // off, we will explicitly read it. - if (!prefs.getBoolean(KEY_HOME_TZ_ENABLED, false)) { - mTimeZoneId = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()); - } - - mHomeTZ.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - showTimezoneDialog(); - return true; - } - }); - - if (mTzPickerUtils == null) { - mTzPickerUtils = new TimeZonePickerUtils(getActivity()); - } - CharSequence timezoneName = mTzPickerUtils.getGmtDisplayName(getActivity(), mTimeZoneId, - System.currentTimeMillis(), false); - mHomeTZ.setSummary(timezoneName != null ? timezoneName : mTimeZoneId); - - TimeZonePickerDialog tzpd = (TimeZonePickerDialog) activity.getFragmentManager() - .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER); - if (tzpd != null) { - tzpd.setOnTimeZoneSetListener(this); - } - - migrateOldPreferences(sharedPreferences); - - updateChildPreferences(); - } - - private void showTimezoneDialog() { - final Activity activity = getActivity(); - if (activity == null) { - return; - } - - Bundle b = new Bundle(); - b.putLong(TimeZonePickerDialog.BUNDLE_START_TIME_MILLIS, System.currentTimeMillis()); - b.putString(TimeZonePickerDialog.BUNDLE_TIME_ZONE, Utils.getTimeZone(activity, null)); - - FragmentManager fm = getActivity().getFragmentManager(); - TimeZonePickerDialog tzpd = (TimeZonePickerDialog) fm - .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER); - if (tzpd != null) { - tzpd.dismiss(); - } - tzpd = new TimeZonePickerDialog(); - tzpd.setArguments(b); - tzpd.setOnTimeZoneSetListener(this); - tzpd.show(fm, FRAG_TAG_TIME_ZONE_PICKER); - } - - @Override - public void onStart() { - super.onStart(); - getPreferenceScreen().getSharedPreferences() - .registerOnSharedPreferenceChangeListener(this); - setPreferenceListeners(this); - } - - /** - * Sets up all the preference change listeners to use the specified - * listener. - */ - private void setPreferenceListeners(OnPreferenceChangeListener listener) { - mUseHomeTZ.setOnPreferenceChangeListener(listener); - mHomeTZ.setOnPreferenceChangeListener(listener); - mWeekStart.setOnPreferenceChangeListener(listener); - mDefaultReminder.setOnPreferenceChangeListener(listener); - mHideDeclined.setOnPreferenceChangeListener(listener); - mVibrate.setOnPreferenceChangeListener(listener); - } - - @Override - public void onStop() { - getPreferenceScreen().getSharedPreferences() - .unregisterOnSharedPreferenceChangeListener(this); - setPreferenceListeners(null); - super.onStop(); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - Activity a = getActivity(); - if (key.equals(KEY_ALERTS)) { - updateChildPreferences(); - if (a != null) { - Intent intent = new Intent(); - intent.setClass(a, AlertReceiver.class); - if (mAlert.isChecked()) { - intent.setAction(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS); - } else { - intent.setAction(AlertReceiver.EVENT_REMINDER_APP_ACTION); - } - a.sendBroadcast(intent); - } - } - if (a != null) { - BackupManager.dataChanged(a.getPackageName()); - } - } - - /** - * Handles time zone preference changes - */ - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String tz; - final Activity activity = getActivity(); - if (preference == mUseHomeTZ) { - if ((Boolean)newValue) { - tz = mTimeZoneId; - } else { - tz = CalendarCache.TIMEZONE_TYPE_AUTO; - } - Utils.setTimeZone(activity, tz); - return true; - } else if (preference == mHideDeclined) { - mHideDeclined.setChecked((Boolean) newValue); - Intent intent = new Intent(Utils.getWidgetScheduledUpdateAction(activity)); - intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE); - activity.sendBroadcast(intent); - return true; - } else if (preference == mWeekStart) { - mWeekStart.setValue((String) newValue); - mWeekStart.setSummary(mWeekStart.getEntry()); - } else if (preference == mDefaultReminder) { - mDefaultReminder.setValue((String) newValue); - mDefaultReminder.setSummary(mDefaultReminder.getEntry()); - } else if (preference == mVibrate) { - mVibrate.setChecked((Boolean) newValue); - return true; - } else { - return true; - } - return false; - } - - public String getRingtoneTitleFromUri(Context context, String uri) { - if (TextUtils.isEmpty(uri)) { - return null; - } - - Ringtone ring = RingtoneManager.getRingtone(getActivity(), Uri.parse(uri)); - if (ring != null) { - return ring.getTitle(context); - } - return null; - } - - /** - * If necessary, upgrades previous versions of preferences to the current - * set of keys and values. - * @param prefs the preferences to upgrade - */ - private void migrateOldPreferences(SharedPreferences prefs) { - // If needed, migrate vibration setting from a previous version - - mVibrate.setChecked(Utils.getDefaultVibrate(getActivity(), prefs)); - - // If needed, migrate the old alerts type settin - if (!prefs.contains(KEY_ALERTS) && prefs.contains(KEY_ALERTS_TYPE)) { - String type = prefs.getString(KEY_ALERTS_TYPE, ALERT_TYPE_STATUS_BAR); - if (type.equals(ALERT_TYPE_OFF)) { - mAlert.setChecked(false); - mPopup.setChecked(false); - mPopup.setEnabled(false); - } else if (type.equals(ALERT_TYPE_STATUS_BAR)) { - mAlert.setChecked(true); - mPopup.setChecked(false); - mPopup.setEnabled(true); - } else if (type.equals(ALERT_TYPE_ALERTS)) { - mAlert.setChecked(true); - mPopup.setChecked(true); - mPopup.setEnabled(true); - } - // clear out the old setting - prefs.edit().remove(KEY_ALERTS_TYPE).commit(); - } - } - - /** - * Keeps the dependent settings in sync with the parent preference, so for - * example, when notifications are turned off, we disable the preferences - * for configuring the exact notification behavior. - */ - private void updateChildPreferences() { - if (mAlert.isChecked()) { - mVibrate.setEnabled(true); - mPopup.setEnabled(true); - } else { - mVibrate.setEnabled(false); - mPopup.setEnabled(false); - } - } - - - @Override - public boolean onPreferenceTreeClick( - PreferenceScreen preferenceScreen, Preference preference) { - final String key = preference.getKey(); - return super.onPreferenceTreeClick(preferenceScreen, preference); - } - - @Override - public void onTimeZoneSet(TimeZoneInfo tzi) { - if (mTzPickerUtils == null) { - mTzPickerUtils = new TimeZonePickerUtils(getActivity()); - } - - final CharSequence timezoneName = mTzPickerUtils.getGmtDisplayName( - getActivity(), tzi.mTzId, System.currentTimeMillis(), false); - mHomeTZ.setSummary(timezoneName); - Utils.setTimeZone(getActivity(), tzi.mTzId); - } -} diff --git a/src/com/android/calendar/GoogleCalendarUriIntentFilter.java b/src/com/android/calendar/GoogleCalendarUriIntentFilter.java deleted file mode 100644 index 3970115b..00000000 --- a/src/com/android/calendar/GoogleCalendarUriIntentFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* -** -** 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 android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.os.Bundle; - -public class GoogleCalendarUriIntentFilter extends Activity { - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - Intent intent = getIntent(); - if (intent != null) { - // 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/MultiStateButton.java b/src/com/android/calendar/MultiStateButton.java deleted file mode 100644 index 8034b28e..00000000 --- a/src/com/android/calendar/MultiStateButton.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2010 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.Context; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Gravity; -import android.widget.Button; - -/** - *

- * A button with more than two states. When the button is pressed - * or clicked, the state transitions automatically. - *

- * - *

XML attributes

- *

- * See {@link R.styleable#MultiStateButton - * MultiStateButton Attributes}, {@link android.R.styleable#Button Button - * Attributes}, {@link android.R.styleable#TextView TextView Attributes}, {@link - * android.R.styleable#View View Attributes} - *

- */ - -public class MultiStateButton extends Button { - //The current state for this button, ranging from 0 to maxState-1 - private int mState; - //The maximum number of states allowed for this button. - private int mMaxStates; - //The currently displaying resource ID. This gets set to a default on creation and remains - //on the last set if the resources get set to null. - private int mButtonResource; - //A list of all drawable resources used by this button in the order it uses them. - private int[] mButtonResources; - private Drawable mButtonDrawable; - - public MultiStateButton(Context context) { - this(context, null); - } - - public MultiStateButton(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public MultiStateButton(Context context, AttributeSet attrs, int defStyle) { - //Currently using the standard buttonStyle, will update when new resources are added. - super(context, attrs, defStyle); - mMaxStates = 1; - mState = 0; - //TODO add a more generic default button - mButtonResources = new int[] { R.drawable.widget_show }; - setButtonDrawable(mButtonResources[mState]); - } - - @Override - public boolean performClick() { - /* When clicked, toggle the state */ - transitionState(); - return super.performClick(); - } - - public void transitionState() { - mState = (mState + 1) % mMaxStates; - setButtonDrawable(mButtonResources[mState]); - } - - /** - * Allows for a new set of drawable resource ids to be set. - * - * This sets the maximum states allowed to the length of the resources array. It will also - * set the current state to the maximum allowed if it's greater than the new max. - */ - public void setButtonResources(int[] resources) throws IllegalArgumentException { - if(resources == null) { - throw new IllegalArgumentException("Button resources cannot be null"); - } - mMaxStates = resources.length; - if(mState >= mMaxStates) { - mState = mMaxStates - 1; - } - mButtonResources = resources; - } - - /** - * Attempts to set the state. Returns true if successful, false otherwise. - */ - public boolean setState(int state){ - if(state >= mMaxStates || state < 0) { - //When moved out of Calendar the tag should be changed. - Log.w("Cal", "MultiStateButton state set to value greater than maxState or < 0"); - return false; - } - mState = state; - setButtonDrawable(mButtonResources[mState]); - return true; - } - - public int getState() { - return mState; - } - - /** - * Set the background to a given Drawable, identified by its resource id. - * - * @param resid the resource id of the drawable to use as the background - */ - public void setButtonDrawable(int resid) { - if (resid != 0 && resid == mButtonResource) { - return; - } - - mButtonResource = resid; - - Drawable d = null; - if (mButtonResource != 0) { - d = getResources().getDrawable(mButtonResource); - } - setButtonDrawable(d); - } - - /** - * Set the background to a given Drawable - * - * @param d The Drawable to use as the background - */ - public void setButtonDrawable(Drawable d) { - if (d != null) { - if (mButtonDrawable != null) { - mButtonDrawable.setCallback(null); - unscheduleDrawable(mButtonDrawable); - } - d.setCallback(this); - d.setState(getDrawableState()); - d.setVisible(getVisibility() == VISIBLE, false); - mButtonDrawable = d; - mButtonDrawable.setState(null); - setMinHeight(mButtonDrawable.getIntrinsicHeight()); - setWidth(mButtonDrawable.getIntrinsicWidth()); - } - refreshDrawableState(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (mButtonDrawable != null) { - final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; - final int horizontalGravity = getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK; - final int height = mButtonDrawable.getIntrinsicHeight(); - final int width = mButtonDrawable.getIntrinsicWidth(); - - int y = 0; - int x = 0; - - switch (verticalGravity) { - case Gravity.BOTTOM: - y = getHeight() - height; - break; - case Gravity.CENTER_VERTICAL: - y = (getHeight() - height) / 2; - break; - } - switch (horizontalGravity) { - case Gravity.RIGHT: - x = getWidth() - width; - break; - case Gravity.CENTER_HORIZONTAL: - x = (getWidth() - width) / 2; - break; - } - - mButtonDrawable.setBounds(x, y, x + width, y + height); - mButtonDrawable.draw(canvas); - } - } -} diff --git a/src/com/android/calendar/OtherPreferences.java b/src/com/android/calendar/OtherPreferences.java deleted file mode 100644 index a59d3f46..00000000 --- a/src/com/android/calendar/OtherPreferences.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2011 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.app.Activity; -import android.app.Dialog; -import android.app.TimePickerDialog; -import android.content.ComponentName; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.text.format.DateFormat; -import android.text.format.Time; -import android.util.Log; -import android.widget.TimePicker; - -public class OtherPreferences extends PreferenceFragment implements OnPreferenceChangeListener{ - private static final String TAG = "CalendarOtherPreferences"; - - // The name of the shared preferences file. This name must be maintained for - // historical reasons, as it's what PreferenceManager assigned the first - // time the file was created. - static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; - - // Must be the same keys that are used in the other_preferences.xml file. - public static final String KEY_OTHER_COPY_DB = "preferences_copy_db"; - public static final String KEY_OTHER_QUIET_HOURS = "preferences_reminders_quiet_hours"; - public static final String KEY_OTHER_REMINDERS_RESPONDED = "preferences_reminders_responded"; - public static final String KEY_OTHER_QUIET_HOURS_START = - "preferences_reminders_quiet_hours_start"; - public static final String KEY_OTHER_QUIET_HOURS_START_HOUR = - "preferences_reminders_quiet_hours_start_hour"; - public static final String KEY_OTHER_QUIET_HOURS_START_MINUTE = - "preferences_reminders_quiet_hours_start_minute"; - public static final String KEY_OTHER_QUIET_HOURS_END = - "preferences_reminders_quiet_hours_end"; - public static final String KEY_OTHER_QUIET_HOURS_END_HOUR = - "preferences_reminders_quiet_hours_end_hour"; - public static final String KEY_OTHER_QUIET_HOURS_END_MINUTE = - "preferences_reminders_quiet_hours_end_minute"; - public static final String KEY_OTHER_1 = "preferences_tardis_1"; - - public static final int QUIET_HOURS_DEFAULT_START_HOUR = 22; - public static final int QUIET_HOURS_DEFAULT_START_MINUTE = 0; - public static final int QUIET_HOURS_DEFAULT_END_HOUR = 8; - public static final int QUIET_HOURS_DEFAULT_END_MINUTE = 0; - - private static final int START_LISTENER = 1; - private static final int END_LISTENER = 2; - private static final String format24Hour = "%H:%M"; - private static final String format12Hour = "%I:%M%P"; - - private Preference mCopyDb; - private CheckBoxPreference mQuietHours; - private Preference mQuietHoursStart; - private Preference mQuietHoursEnd; - - private TimePickerDialog mTimePickerDialog; - private TimeSetListener mQuietHoursStartListener; - private TimePickerDialog mQuietHoursStartDialog; - private TimeSetListener mQuietHoursEndListener; - private TimePickerDialog mQuietHoursEndDialog; - private boolean mIs24HourMode; - - public OtherPreferences() { - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - PreferenceManager manager = getPreferenceManager(); - manager.setSharedPreferencesName(SHARED_PREFS_NAME); - SharedPreferences prefs = manager.getSharedPreferences(); - - addPreferencesFromResource(R.xml.other_preferences); - mCopyDb = findPreference(KEY_OTHER_COPY_DB); - - Activity activity = getActivity(); - if (activity == null) { - Log.d(TAG, "Activity was null"); - } - mIs24HourMode = DateFormat.is24HourFormat(activity); - - mQuietHours = - (CheckBoxPreference) findPreference(KEY_OTHER_QUIET_HOURS); - - int startHour = prefs.getInt(KEY_OTHER_QUIET_HOURS_START_HOUR, - QUIET_HOURS_DEFAULT_START_HOUR); - int startMinute = prefs.getInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, - QUIET_HOURS_DEFAULT_START_MINUTE); - mQuietHoursStart = findPreference(KEY_OTHER_QUIET_HOURS_START); - mQuietHoursStartListener = new TimeSetListener(START_LISTENER); - mQuietHoursStartDialog = new TimePickerDialog( - activity, mQuietHoursStartListener, - startHour, startMinute, mIs24HourMode); - mQuietHoursStart.setSummary(formatTime(startHour, startMinute)); - - int endHour = prefs.getInt(KEY_OTHER_QUIET_HOURS_END_HOUR, - QUIET_HOURS_DEFAULT_END_HOUR); - int endMinute = prefs.getInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, - QUIET_HOURS_DEFAULT_END_MINUTE); - mQuietHoursEnd = findPreference(KEY_OTHER_QUIET_HOURS_END); - mQuietHoursEndListener = new TimeSetListener(END_LISTENER); - mQuietHoursEndDialog = new TimePickerDialog( - activity, mQuietHoursEndListener, - endHour, endMinute, mIs24HourMode); - mQuietHoursEnd.setSummary(formatTime(endHour, endMinute)); - } - - @Override - public boolean onPreferenceChange(Preference preference, Object objValue) { - return true; - } - - @Override - public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { - if (preference == mCopyDb) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setComponent(new ComponentName("com.android.providers.calendar", - "com.android.providers.calendar.CalendarDebugActivity")); - startActivity(intent); - } else if (preference == mQuietHoursStart) { - if (mTimePickerDialog == null) { - mTimePickerDialog = mQuietHoursStartDialog; - mTimePickerDialog.show(); - } else { - Log.v(TAG, "not null"); - } - } else if (preference == mQuietHoursEnd) { - if (mTimePickerDialog == null) { - mTimePickerDialog = mQuietHoursEndDialog; - mTimePickerDialog.show(); - } else { - Log.v(TAG, "not null"); - } - } else { - return super.onPreferenceTreeClick(screen, preference); - } - return true; - } - - private class TimeSetListener implements TimePickerDialog.OnTimeSetListener { - private int mListenerId; - - public TimeSetListener(int listenerId) { - mListenerId = listenerId; - } - - @Override - public void onTimeSet(TimePicker view, int hourOfDay, int minute) { - mTimePickerDialog = null; - - SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); - SharedPreferences.Editor editor = prefs.edit(); - - String summary = formatTime(hourOfDay, minute); - switch (mListenerId) { - case (START_LISTENER): - mQuietHoursStart.setSummary(summary); - editor.putInt(KEY_OTHER_QUIET_HOURS_START_HOUR, hourOfDay); - editor.putInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, minute); - break; - case (END_LISTENER): - mQuietHoursEnd.setSummary(summary); - editor.putInt(KEY_OTHER_QUIET_HOURS_END_HOUR, hourOfDay); - editor.putInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, minute); - break; - default: - Log.d(TAG, "Set time for unknown listener: "+mListenerId); - } - - editor.commit(); - } - } - - /** - * @param hourOfDay the hour of the day (0-24) - * @param minute - * @return human-readable string formatted based on 24-hour mode. - */ - private String formatTime(int hourOfDay, int minute) { - Time time = new Time(); - time.hour = hourOfDay; - time.minute = minute; - - String format = mIs24HourMode? format24Hour : format12Hour; - return time.format(format); - } -} diff --git a/src/com/android/calendar/StickyHeaderListView.java b/src/com/android/calendar/StickyHeaderListView.java deleted file mode 100644 index 981e7af7..00000000 --- a/src/com/android/calendar/StickyHeaderListView.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright (C) 2011 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.Context; -import android.graphics.Color; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; -import android.widget.Adapter; -import android.widget.FrameLayout; -import android.widget.ListView; - -/** - * Implements a ListView class with a sticky header at the top. The header is - * per section and it is pinned to the top as long as its section is at the top - * of the view. If it is not, the header slides up or down (depending on the - * scroll movement) and the header of the current section slides to the top. - * Notes: - * 1. The class uses the first available child ListView as the working - * ListView. If no ListView child exists, the class will create a default one. - * 2. The ListView's adapter must be passed to this class using the 'setAdapter' - * method. The adapter must implement the HeaderIndexer interface. If no adapter - * is specified, the class will try to extract it from the ListView - * 3. The class registers itself as a listener to scroll events (OnScrollListener), if the - * ListView needs to receive scroll events, it must register its listener using - * this class' setOnScrollListener method. - * 4. Headers for the list view must be added before using the StickyHeaderListView - * 5. The implementation should register to listen to dataset changes. Right now this is not done - * since a change the dataset in a listview forces a call to OnScroll. The needed code is - * commented out. - */ -public class StickyHeaderListView extends FrameLayout implements OnScrollListener { - - private static final String TAG = "StickyHeaderListView"; - protected boolean mChildViewsCreated = false; - protected boolean mDoHeaderReset = false; - - protected Context mContext = null; - protected Adapter mAdapter = null; - protected HeaderIndexer mIndexer = null; - protected HeaderHeightListener mHeaderHeightListener = null; - protected View mStickyHeader = null; - protected View mNonessentialHeader = null; // A invisible header used when a section has no header - protected ListView mListView = null; - protected ListView.OnScrollListener mListener = null; - - private int mSeparatorWidth; - private View mSeparatorView; - private int mLastStickyHeaderHeight = 0; - - // This code is needed only if dataset changes do not force a call to OnScroll - // protected DataSetObserver mListDataObserver = null; - - - protected int mCurrentSectionPos = -1; // Position of section that has its header on the - // top of the view - protected int mNextSectionPosition = -1; // Position of next section's header - protected int mListViewHeadersCount = 0; - - /** - * Interface that must be implemented by the ListView adapter to provide headers locations - * and number of items under each header. - * - */ - public interface HeaderIndexer { - /** - * Calculates the position of the header of a specific item in the adapter's data set. - * For example: Assuming you have a list with albums and songs names: - * Album A, song 1, song 2, ...., song 10, Album B, song 1, ..., song 7. A call to - * this method with the position of song 5 in Album B, should return the position - * of Album B. - * @param position - Position of the item in the ListView dataset - * @return Position of header. -1 if the is no header - */ - - int getHeaderPositionFromItemPosition(int position); - - /** - * Calculates the number of items in the section defined by the header (not including - * the header). - * For example: A list with albums and songs, the method should return - * the number of songs names (without the album name). - * - * @param headerPosition - the value returned by 'getHeaderPositionFromItemPosition' - * @return Number of items. -1 on error. - */ - int getHeaderItemsNumber(int headerPosition); - } - - /*** - * - * Interface that is used to update the sticky header's height - * - */ - public interface HeaderHeightListener { - - /*** - * Updated a change in the sticky header's size - * - * @param height - new height of sticky header - */ - void OnHeaderHeightChanged(int height); - } - - /** - * Sets the adapter to be used by the class to get views of headers - * - * @param adapter - The adapter. - */ - - public void setAdapter(Adapter adapter) { - - // This code is needed only if dataset changes do not force a call to - // OnScroll - // if (mAdapter != null && mListDataObserver != null) { - // mAdapter.unregisterDataSetObserver(mListDataObserver); - // } - - if (adapter != null) { - mAdapter = adapter; - // This code is needed only if dataset changes do not force a call - // to OnScroll - // mAdapter.registerDataSetObserver(mListDataObserver); - } - } - - /** - * Sets the indexer object (that implements the HeaderIndexer interface). - * - * @param indexer - The indexer. - */ - - public void setIndexer(HeaderIndexer indexer) { - mIndexer = indexer; - } - - /** - * Sets the list view that is displayed - * @param lv - The list view. - */ - - public void setListView(ListView lv) { - mListView = lv; - mListView.setOnScrollListener(this); - mListViewHeadersCount = mListView.getHeaderViewsCount(); - } - - /** - * Sets an external OnScroll listener. Since the StickyHeaderListView sets - * itself as the scroll events listener of the listview, this method allows - * the user to register another listener that will be called after this - * class listener is called. - * - * @param listener - The external listener. - */ - public void setOnScrollListener(ListView.OnScrollListener listener) { - mListener = listener; - } - - public void setHeaderHeightListener(HeaderHeightListener listener) { - mHeaderHeightListener = listener; - } - - // This code is needed only if dataset changes do not force a call to OnScroll - // protected void createDataListener() { - // mListDataObserver = new DataSetObserver() { - // @Override - // public void onChanged() { - // onDataChanged(); - // } - // }; - // } - - /** - * Constructor - * - * @param context - application context. - * @param attrs - layout attributes. - */ - public StickyHeaderListView(Context context, AttributeSet attrs) { - super(context, attrs); - mContext = context; - // This code is needed only if dataset changes do not force a call to OnScroll - // createDataListener(); - } - - /** - * Scroll status changes listener - * - * @param view - the scrolled view - * @param scrollState - new scroll state. - */ - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - if (mListener != null) { - mListener.onScrollStateChanged(view, scrollState); - } - } - - /** - * Scroll events listener - * - * @param view - the scrolled view - * @param firstVisibleItem - the index (in the list's adapter) of the top - * visible item. - * @param visibleItemCount - the number of visible items in the list - * @param totalItemCount - the total number items in the list - */ - @Override - public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, - int totalItemCount) { - - updateStickyHeader(firstVisibleItem); - - if (mListener != null) { - mListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); - } - } - - /** - * Sets a separator below the sticky header, which will be visible while the sticky header - * is not scrolling up. - * @param color - color of separator - * @param width - width in pixels of separator - */ - public void setHeaderSeparator(int color, int width) { - mSeparatorView = new View(mContext); - ViewGroup.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, - width, Gravity.TOP); - mSeparatorView.setLayoutParams(params); - mSeparatorView.setBackgroundColor(color); - mSeparatorWidth = width; - this.addView(mSeparatorView); - } - - protected void updateStickyHeader(int firstVisibleItem) { - - // Try to make sure we have an adapter to work with (may not succeed). - if (mAdapter == null && mListView != null) { - setAdapter(mListView.getAdapter()); - } - - firstVisibleItem -= mListViewHeadersCount; - if (mAdapter != null && mIndexer != null && mDoHeaderReset) { - - // Get the section header position - int sectionSize = 0; - int sectionPos = mIndexer.getHeaderPositionFromItemPosition(firstVisibleItem); - - // New section - set it in the header view - boolean newView = false; - if (sectionPos != mCurrentSectionPos) { - - // No header for current position , use the nonessential invisible one, hide the separator - if (sectionPos == -1) { - sectionSize = 0; - this.removeView(mStickyHeader); - mStickyHeader = mNonessentialHeader; - if (mSeparatorView != null) { - mSeparatorView.setVisibility(View.GONE); - } - newView = true; - } else { - // Create a copy of the header view to show on top - sectionSize = mIndexer.getHeaderItemsNumber(sectionPos); - View v = mAdapter.getView(sectionPos + mListViewHeadersCount, null, mListView); - v.measure(MeasureSpec.makeMeasureSpec(mListView.getWidth(), - MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mListView.getHeight(), - MeasureSpec.AT_MOST)); - this.removeView(mStickyHeader); - mStickyHeader = v; - newView = true; - } - mCurrentSectionPos = sectionPos; - mNextSectionPosition = sectionSize + sectionPos + 1; - } - - - // Do transitions - // If position of bottom of last item in a section is smaller than the height of the - // sticky header - shift drawable of header. - if (mStickyHeader != null) { - int sectionLastItemPosition = mNextSectionPosition - firstVisibleItem - 1; - int stickyHeaderHeight = mStickyHeader.getHeight(); - if (stickyHeaderHeight == 0) { - stickyHeaderHeight = mStickyHeader.getMeasuredHeight(); - } - - // Update new header height - if (mHeaderHeightListener != null && - mLastStickyHeaderHeight != stickyHeaderHeight) { - mLastStickyHeaderHeight = stickyHeaderHeight; - mHeaderHeightListener.OnHeaderHeightChanged(stickyHeaderHeight); - } - - View SectionLastView = mListView.getChildAt(sectionLastItemPosition); - if (SectionLastView != null && SectionLastView.getBottom() <= stickyHeaderHeight) { - int lastViewBottom = SectionLastView.getBottom(); - mStickyHeader.setTranslationY(lastViewBottom - stickyHeaderHeight); - if (mSeparatorView != null) { - mSeparatorView.setVisibility(View.GONE); - } - } else if (stickyHeaderHeight != 0) { - mStickyHeader.setTranslationY(0); - if (mSeparatorView != null && !mStickyHeader.equals(mNonessentialHeader)) { - mSeparatorView.setVisibility(View.VISIBLE); - } - } - if (newView) { - mStickyHeader.setVisibility(View.INVISIBLE); - this.addView(mStickyHeader); - if (mSeparatorView != null && !mStickyHeader.equals(mNonessentialHeader)){ - FrameLayout.LayoutParams params = - new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, - mSeparatorWidth); - params.setMargins(0, mStickyHeader.getMeasuredHeight(), 0, 0); - mSeparatorView.setLayoutParams(params); - mSeparatorView.setVisibility(View.VISIBLE); - } - mStickyHeader.setVisibility(View.VISIBLE); - } - } - } - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - if (!mChildViewsCreated) { - setChildViews(); - } - mDoHeaderReset = true; - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (!mChildViewsCreated) { - setChildViews(); - } - mDoHeaderReset = true; - } - - - // Resets the sticky header when the adapter data set was changed - // This code is needed only if dataset changes do not force a call to OnScroll - // protected void onDataChanged() { - // Should do a call to updateStickyHeader if needed - // } - - private void setChildViews() { - - // Find a child ListView (if any) - int iChildNum = getChildCount(); - for (int i = 0; i < iChildNum; i++) { - Object v = getChildAt(i); - if (v instanceof ListView) { - setListView((ListView) v); - } - } - - // No child ListView - add one - if (mListView == null) { - setListView(new ListView(mContext)); - } - - // Create a nonessential view , it will be used in case a section has no header - mNonessentialHeader = new View (mContext); - ViewGroup.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, - 1, Gravity.TOP); - mNonessentialHeader.setLayoutParams(params); - mNonessentialHeader.setBackgroundColor(Color.TRANSPARENT); - - mChildViewsCreated = true; - } - -} diff --git a/src/com/android/calendar/UpgradeReceiver.java b/src/com/android/calendar/UpgradeReceiver.java deleted file mode 100644 index 0e89286d..00000000 --- a/src/com/android/calendar/UpgradeReceiver.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class UpgradeReceiver extends BroadcastReceiver { - @Override - public void onReceive(final Context context, final Intent intent) { - Utils.trySyncAndDisableUpgradeReceiver(context); - } - -} \ No newline at end of file diff --git a/src/com/android/calendar/Utils.java b/src/com/android/calendar/Utils.java deleted file mode 100644 index cc55c999..00000000 --- a/src/com/android/calendar/Utils.java +++ /dev/null @@ -1,1499 +0,0 @@ -/* - * Copyright (C) 2006 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 static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; - -import android.accounts.Account; -import android.app.Activity; -import android.app.SearchManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.graphics.Color; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.provider.CalendarContract.Calendars; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.text.style.URLSpan; -import android.text.util.Linkify; -import android.util.Log; - -import com.android.calendar.CalendarController.ViewType; -import com.android.calendar.CalendarUtils.TimeZoneUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Formatter; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class Utils { - private static final boolean DEBUG = false; - private static final String TAG = "CalUtils"; - - // Set to 0 until we have UI to perform undo - public static final long UNDO_DELAY = 0; - - // For recurring events which instances of the series are being modified - public static final int MODIFY_UNINITIALIZED = 0; - public static final int MODIFY_SELECTED = 1; - public static final int MODIFY_ALL_FOLLOWING = 2; - public static final int MODIFY_ALL = 3; - - // When the edit event view finishes it passes back the appropriate exit - // code. - public static final int DONE_REVERT = 1 << 0; - public static final int DONE_SAVE = 1 << 1; - public static final int DONE_DELETE = 1 << 2; - // And should re run with DONE_EXIT if it should also leave the view, just - // exiting is identical to reverting - public static final int DONE_EXIT = 1 << 0; - - public static final String OPEN_EMAIL_MARKER = " <"; - public static final String CLOSE_EMAIL_MARKER = ">"; - - public static final String INTENT_KEY_DETAIL_VIEW = "DETAIL_VIEW"; - public static final String INTENT_KEY_VIEW_TYPE = "VIEW"; - public static final String INTENT_VALUE_VIEW_TYPE_DAY = "DAY"; - public static final String INTENT_KEY_HOME = "KEY_HOME"; - - public static final int MONDAY_BEFORE_JULIAN_EPOCH = Time.EPOCH_JULIAN_DAY - 3; - public static final int DECLINED_EVENT_ALPHA = 0x66; - public static final int DECLINED_EVENT_TEXT_ALPHA = 0xC0; - - private static final float SATURATION_ADJUST = 1.3f; - private static final float INTENSITY_ADJUST = 0.8f; - - // Defines used by the DNA generation code - static final int DAY_IN_MINUTES = 60 * 24; - static final int WEEK_IN_MINUTES = DAY_IN_MINUTES * 7; - // The work day is being counted as 6am to 8pm - static int WORK_DAY_MINUTES = 14 * 60; - static int WORK_DAY_START_MINUTES = 6 * 60; - static int WORK_DAY_END_MINUTES = 20 * 60; - static int WORK_DAY_END_LENGTH = (24 * 60) - WORK_DAY_END_MINUTES; - static int CONFLICT_COLOR = 0xFF000000; - static boolean mMinutesLoaded = false; - - public static final int YEAR_MIN = 1970; - public static final int YEAR_MAX = 2036; - - // The name of the shared preferences file. This name must be maintained for - // historical - // reasons, as it's what PreferenceManager assigned the first time the file - // was created. - static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; - - public static final String KEY_QUICK_RESPONSES = "preferences_quick_responses"; - - public static final String KEY_ALERTS_VIBRATE_WHEN = "preferences_alerts_vibrateWhen"; - - public static final String APPWIDGET_DATA_TYPE = "vnd.android.data/update"; - - static final String MACHINE_GENERATED_ADDRESS = "calendar.google.com"; - - private static final TimeZoneUtils mTZUtils = new TimeZoneUtils(SHARED_PREFS_NAME); - private static boolean mAllowWeekForDetailView = false; - private static long mTardis = 0; - private static String sVersion = null; - - private static final Pattern mWildcardPattern = Pattern.compile("^.*$"); - - /** - * A coordinate must be of the following form for Google Maps to correctly use it: - * Latitude, Longitude - * - * This may be in decimal form: - * Latitude: {-90 to 90} - * Longitude: {-180 to 180} - * - * Or, in degrees, minutes, and seconds: - * Latitude: {-90 to 90}° {0 to 59}' {0 to 59}" - * Latitude: {-180 to 180}° {0 to 59}' {0 to 59}" - * + or - degrees may also be represented with N or n, S or s for latitude, and with - * E or e, W or w for longitude, where the direction may either precede or follow the value. - * - * Some examples of coordinates that will be accepted by the regex: - * 37.422081°, -122.084576° - * 37.422081,-122.084576 - * +37°25'19.49", -122°5'4.47" - * 37°25'19.49"N, 122°5'4.47"W - * N 37° 25' 19.49", W 122° 5' 4.47" - **/ - private static final String COORD_DEGREES_LATITUDE = - "([-+NnSs]" + "(\\s)*)?" - + "[1-9]?[0-9](\u00B0)" + "(\\s)*" - + "([1-5]?[0-9]\')?" + "(\\s)*" - + "([1-5]?[0-9]" + "(\\.[0-9]+)?\")?" - + "((\\s)*" + "[NnSs])?"; - private static final String COORD_DEGREES_LONGITUDE = - "([-+EeWw]" + "(\\s)*)?" - + "(1)?[0-9]?[0-9](\u00B0)" + "(\\s)*" - + "([1-5]?[0-9]\')?" + "(\\s)*" - + "([1-5]?[0-9]" + "(\\.[0-9]+)?\")?" - + "((\\s)*" + "[EeWw])?"; - private static final String COORD_DEGREES_PATTERN = - COORD_DEGREES_LATITUDE - + "(\\s)*" + "," + "(\\s)*" - + COORD_DEGREES_LONGITUDE; - private static final String COORD_DECIMAL_LATITUDE = - "[+-]?" - + "[1-9]?[0-9]" + "(\\.[0-9]+)" - + "(\u00B0)?"; - private static final String COORD_DECIMAL_LONGITUDE = - "[+-]?" - + "(1)?[0-9]?[0-9]" + "(\\.[0-9]+)" - + "(\u00B0)?"; - private static final String COORD_DECIMAL_PATTERN = - COORD_DECIMAL_LATITUDE - + "(\\s)*" + "," + "(\\s)*" - + COORD_DECIMAL_LONGITUDE; - private static final Pattern COORD_PATTERN = - Pattern.compile(COORD_DEGREES_PATTERN + "|" + COORD_DECIMAL_PATTERN); - - private static final String NANP_ALLOWED_SYMBOLS = "()+-*#."; - private static final int NANP_MIN_DIGITS = 7; - private static final int NANP_MAX_DIGITS = 11; - - - /** - * Returns whether the SDK is the Jellybean release or later. - */ - public static boolean isJellybeanOrLater() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; - } - - /** - * Returns whether the SDK is the KeyLimePie release or later. - */ - public static boolean isKeyLimePieOrLater() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - } - - public static int getViewTypeFromIntentAndSharedPref(Activity activity) { - Intent intent = activity.getIntent(); - Bundle extras = intent.getExtras(); - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(activity); - - if (TextUtils.equals(intent.getAction(), Intent.ACTION_EDIT)) { - return ViewType.EDIT; - } - if (extras != null) { - if (extras.getBoolean(INTENT_KEY_DETAIL_VIEW, false)) { - // This is the "detail" view which is either agenda or day view - return prefs.getInt(GeneralPreferences.KEY_DETAILED_VIEW, - GeneralPreferences.DEFAULT_DETAILED_VIEW); - } else if (INTENT_VALUE_VIEW_TYPE_DAY.equals(extras.getString(INTENT_KEY_VIEW_TYPE))) { - // Not sure who uses this. This logic came from LaunchActivity - return ViewType.DAY; - } - } - - // Default to the last view - return prefs.getInt( - GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW); - } - - /** - * Gets the intent action for telling the widget to update. - */ - public static String getWidgetUpdateAction(Context context) { - return context.getPackageName() + ".APPWIDGET_UPDATE"; - } - - /** - * Gets the intent action for telling the widget to update. - */ - public static String getWidgetScheduledUpdateAction(Context context) { - return context.getPackageName() + ".APPWIDGET_SCHEDULED_UPDATE"; - } - - /** - * Writes a new home time zone to the db. Updates the home time zone in the - * db asynchronously and updates the local cache. Sending a time zone of - * **tbd** will cause it to be set to the device's time zone. null or empty - * tz will be ignored. - * - * @param context The calling activity - * @param timeZone The time zone to set Calendar to, or **tbd** - */ - public static void setTimeZone(Context context, String timeZone) { - mTZUtils.setTimeZone(context, timeZone); - } - - /** - * Gets the time zone that Calendar should be displayed in This is a helper - * method to get the appropriate time zone for Calendar. If this is the - * first time this method has been called it will initiate an asynchronous - * query to verify that the data in preferences is correct. The callback - * supplied will only be called if this query returns a value other than - * what is stored in preferences and should cause the calling activity to - * refresh anything that depends on calling this method. - * - * @param context The calling activity - * @param callback The runnable that should execute if a query returns new - * values - * @return The string value representing the time zone Calendar should - * display - */ - public static String getTimeZone(Context context, Runnable callback) { - return mTZUtils.getTimeZone(context, callback); - } - - /** - * Formats a date or a time range according to the local conventions. - * - * @param context the context is required only if the time is shown - * @param startMillis the start time in UTC milliseconds - * @param endMillis the end time in UTC milliseconds - * @param flags a bit mask of options See {@link DateUtils#formatDateRange(Context, Formatter, - * long, long, int, String) formatDateRange} - * @return a string containing the formatted date/time range. - */ - public static String formatDateRange( - Context context, long startMillis, long endMillis, int flags) { - return mTZUtils.formatDateRange(context, startMillis, endMillis, flags); - } - - public static boolean getDefaultVibrate(Context context, SharedPreferences prefs) { - boolean vibrate; - if (prefs.contains(KEY_ALERTS_VIBRATE_WHEN)) { - // Migrate setting to new 4.2 behavior - // - // silent and never -> off - // always -> on - String vibrateWhen = prefs.getString(KEY_ALERTS_VIBRATE_WHEN, null); - vibrate = vibrateWhen != null && vibrateWhen.equals(context - .getString(R.string.prefDefault_alerts_vibrate_true)); - prefs.edit().remove(KEY_ALERTS_VIBRATE_WHEN).commit(); - Log.d(TAG, "Migrating KEY_ALERTS_VIBRATE_WHEN(" + vibrateWhen - + ") to KEY_ALERTS_VIBRATE = " + vibrate); - } else { - vibrate = prefs.getBoolean(GeneralPreferences.KEY_ALERTS_VIBRATE, - false); - } - return vibrate; - } - - public static String[] getSharedPreference(Context context, String key, String[] defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - Set ss = prefs.getStringSet(key, null); - if (ss != null) { - String strings[] = new String[ss.size()]; - return ss.toArray(strings); - } - return defaultValue; - } - - public static String getSharedPreference(Context context, String key, String defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getString(key, defaultValue); - } - - public static int getSharedPreference(Context context, String key, int defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getInt(key, defaultValue); - } - - public static boolean getSharedPreference(Context context, String key, boolean defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getBoolean(key, defaultValue); - } - - /** - * Asynchronously sets the preference with the given key to the given value - * - * @param context the context to use to get preferences from - * @param key the key of the preference to set - * @param value the value to set - */ - public static void setSharedPreference(Context context, String key, String value) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - prefs.edit().putString(key, value).apply(); - } - - public static void setSharedPreference(Context context, String key, String[] values) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - LinkedHashSet set = new LinkedHashSet(); - for (String value : values) { - set.add(value); - } - prefs.edit().putStringSet(key, set).apply(); - } - - protected static void tardis() { - mTardis = System.currentTimeMillis(); - } - - protected static long getTardis() { - return mTardis; - } - - public static void setSharedPreference(Context context, String key, boolean value) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(key, value); - editor.apply(); - } - - static void setSharedPreference(Context context, String key, int value) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - editor.putInt(key, value); - editor.apply(); - } - - public static void removeSharedPreference(Context context, String key) { - SharedPreferences prefs = context.getSharedPreferences( - GeneralPreferences.SHARED_PREFS_NAME, Context.MODE_PRIVATE); - prefs.edit().remove(key).apply(); - } - - /** - * Save default agenda/day/week/month view for next time - * - * @param context - * @param viewId {@link CalendarController.ViewType} - */ - static void setDefaultView(Context context, int viewId) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - - boolean validDetailView = false; - if (mAllowWeekForDetailView && viewId == CalendarController.ViewType.WEEK) { - validDetailView = true; - } else { - validDetailView = viewId == CalendarController.ViewType.AGENDA - || viewId == CalendarController.ViewType.DAY; - } - - if (validDetailView) { - // Record the detail start view - editor.putInt(GeneralPreferences.KEY_DETAILED_VIEW, viewId); - } - - // Record the (new) start view - editor.putInt(GeneralPreferences.KEY_START_VIEW, viewId); - editor.apply(); - } - - public static MatrixCursor matrixCursorFromCursor(Cursor cursor) { - if (cursor == null) { - return null; - } - - String[] columnNames = cursor.getColumnNames(); - if (columnNames == null) { - columnNames = new String[] {}; - } - MatrixCursor newCursor = new MatrixCursor(columnNames); - int numColumns = cursor.getColumnCount(); - String data[] = new String[numColumns]; - cursor.moveToPosition(-1); - while (cursor.moveToNext()) { - for (int i = 0; i < numColumns; i++) { - data[i] = cursor.getString(i); - } - newCursor.addRow(data); - } - return newCursor; - } - - /** - * Compares two cursors to see if they contain the same data. - * - * @return Returns true of the cursors contain the same data and are not - * null, false otherwise - */ - public static boolean compareCursors(Cursor c1, Cursor c2) { - if (c1 == null || c2 == null) { - return false; - } - - int numColumns = c1.getColumnCount(); - if (numColumns != c2.getColumnCount()) { - return false; - } - - if (c1.getCount() != c2.getCount()) { - return false; - } - - c1.moveToPosition(-1); - c2.moveToPosition(-1); - while (c1.moveToNext() && c2.moveToNext()) { - for (int i = 0; i < numColumns; i++) { - if (!TextUtils.equals(c1.getString(i), c2.getString(i))) { - return false; - } - } - } - - return true; - } - - /** - * If the given intent specifies a time (in milliseconds since the epoch), - * then that time is returned. Otherwise, the current time is returned. - */ - public static final long timeFromIntentInMillis(Intent intent) { - // If the time was specified, then use that. Otherwise, use the current - // time. - Uri data = intent.getData(); - long millis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1); - if (millis == -1 && data != null && data.isHierarchical()) { - List path = data.getPathSegments(); - if (path.size() == 2 && path.get(0).equals("time")) { - try { - millis = Long.valueOf(data.getLastPathSegment()); - } catch (NumberFormatException e) { - Log.i("Calendar", "timeFromIntentInMillis: Data existed but no valid time " - + "found. Using current time."); - } - } - } - if (millis <= 0) { - millis = System.currentTimeMillis(); - } - return millis; - } - - /** - * Formats the given Time object so that it gives the month and year (for - * example, "September 2007"). - * - * @param time the time to format - * @return the string containing the weekday and the date - */ - public static String formatMonthYear(Context context, Time time) { - int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY - | DateUtils.FORMAT_SHOW_YEAR; - long millis = time.toMillis(true); - return formatDateRange(context, millis, millis, flags); - } - - /** - * Returns a list joined together by the provided delimiter, for example, - * ["a", "b", "c"] could be joined into "a,b,c" - * - * @param things the things to join together - * @param delim the delimiter to use - * @return a string contained the things joined together - */ - public static String join(List things, String delim) { - StringBuilder builder = new StringBuilder(); - boolean first = true; - for (Object thing : things) { - if (first) { - first = false; - } else { - builder.append(delim); - } - builder.append(thing.toString()); - } - return builder.toString(); - } - - /** - * Returns the week since {@link Time#EPOCH_JULIAN_DAY} (Jan 1, 1970) - * adjusted for first day of week. - * - * This takes a julian day and the week start day and calculates which - * week since {@link Time#EPOCH_JULIAN_DAY} that day occurs in, starting - * at 0. *Do not* use this to compute the ISO week number for the year. - * - * @param julianDay The julian day to calculate the week number for - * @param firstDayOfWeek Which week day is the first day of the week, - * see {@link Time#SUNDAY} - * @return Weeks since the epoch - */ - public static int getWeeksSinceEpochFromJulianDay(int julianDay, int firstDayOfWeek) { - int diff = Time.THURSDAY - firstDayOfWeek; - if (diff < 0) { - diff += 7; - } - int refDay = Time.EPOCH_JULIAN_DAY - diff; - return (julianDay - refDay) / 7; - } - - /** - * Takes a number of weeks since the epoch and calculates the Julian day of - * the Monday for that week. - * - * This assumes that the week containing the {@link Time#EPOCH_JULIAN_DAY} - * is considered week 0. It returns the Julian day for the Monday - * {@code week} weeks after the Monday of the week containing the epoch. - * - * @param week Number of weeks since the epoch - * @return The julian day for the Monday of the given week since the epoch - */ - public static int getJulianMondayFromWeeksSinceEpoch(int week) { - return MONDAY_BEFORE_JULIAN_EPOCH + week * 7; - } - - /** - * Get first day of week as android.text.format.Time constant. - * - * @return the first day of week in android.text.format.Time - */ - public static int getFirstDayOfWeek(Context context) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - String pref = prefs.getString( - GeneralPreferences.KEY_WEEK_START_DAY, GeneralPreferences.WEEK_START_DEFAULT); - - int startDay; - if (GeneralPreferences.WEEK_START_DEFAULT.equals(pref)) { - startDay = Calendar.getInstance().getFirstDayOfWeek(); - } else { - startDay = Integer.parseInt(pref); - } - - if (startDay == Calendar.SATURDAY) { - return Time.SATURDAY; - } else if (startDay == Calendar.MONDAY) { - return Time.MONDAY; - } else { - return Time.SUNDAY; - } - } - - /** - * Get first day of week as java.util.Calendar constant. - * - * @return the first day of week as a java.util.Calendar constant - */ - public static int getFirstDayOfWeekAsCalendar(Context context) { - return convertDayOfWeekFromTimeToCalendar(getFirstDayOfWeek(context)); - } - - /** - * Converts the day of the week from android.text.format.Time to java.util.Calendar - */ - public static int convertDayOfWeekFromTimeToCalendar(int timeDayOfWeek) { - switch (timeDayOfWeek) { - case Time.MONDAY: - return Calendar.MONDAY; - case Time.TUESDAY: - return Calendar.TUESDAY; - case Time.WEDNESDAY: - return Calendar.WEDNESDAY; - case Time.THURSDAY: - return Calendar.THURSDAY; - case Time.FRIDAY: - return Calendar.FRIDAY; - case Time.SATURDAY: - return Calendar.SATURDAY; - case Time.SUNDAY: - return Calendar.SUNDAY; - default: - throw new IllegalArgumentException("Argument must be between Time.SUNDAY and " + - "Time.SATURDAY"); - } - } - - /** - * @return true when week number should be shown. - */ - public static boolean getShowWeekNumber(Context context) { - final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getBoolean( - GeneralPreferences.KEY_SHOW_WEEK_NUM, GeneralPreferences.DEFAULT_SHOW_WEEK_NUM); - } - - /** - * @return true when declined events should be hidden. - */ - public static boolean getHideDeclinedEvents(Context context) { - final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getBoolean(GeneralPreferences.KEY_HIDE_DECLINED, false); - } - - public static int getDaysPerWeek(Context context) { - final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getInt(GeneralPreferences.KEY_DAYS_PER_WEEK, 7); - } - - /** - * Determine whether the column position is Saturday or not. - * - * @param column the column position - * @param firstDayOfWeek the first day of week in android.text.format.Time - * @return true if the column is Saturday position - */ - public static boolean isSaturday(int column, int firstDayOfWeek) { - return (firstDayOfWeek == Time.SUNDAY && column == 6) - || (firstDayOfWeek == Time.MONDAY && column == 5) - || (firstDayOfWeek == Time.SATURDAY && column == 0); - } - - /** - * Determine whether the column position is Sunday or not. - * - * @param column the column position - * @param firstDayOfWeek the first day of week in android.text.format.Time - * @return true if the column is Sunday position - */ - public static boolean isSunday(int column, int firstDayOfWeek) { - return (firstDayOfWeek == Time.SUNDAY && column == 0) - || (firstDayOfWeek == Time.MONDAY && column == 6) - || (firstDayOfWeek == Time.SATURDAY && column == 1); - } - - /** - * Convert given UTC time into current local time. This assumes it is for an - * allday event and will adjust the time to be on a midnight boundary. - * - * @param recycle Time object to recycle, otherwise null. - * @param utcTime Time to convert, in UTC. - * @param tz The time zone to convert this time to. - */ - public static long convertAlldayUtcToLocal(Time recycle, long utcTime, String tz) { - if (recycle == null) { - recycle = new Time(); - } - recycle.timezone = Time.TIMEZONE_UTC; - recycle.set(utcTime); - recycle.timezone = tz; - return recycle.normalize(true); - } - - public static long convertAlldayLocalToUTC(Time recycle, long localTime, String tz) { - if (recycle == null) { - recycle = new Time(); - } - recycle.timezone = tz; - recycle.set(localTime); - recycle.timezone = Time.TIMEZONE_UTC; - return recycle.normalize(true); - } - - /** - * Finds and returns the next midnight after "theTime" in milliseconds UTC - * - * @param recycle - Time object to recycle, otherwise null. - * @param theTime - Time used for calculations (in UTC) - * @param tz The time zone to convert this time to. - */ - public static long getNextMidnight(Time recycle, long theTime, String tz) { - if (recycle == null) { - recycle = new Time(); - } - recycle.timezone = tz; - recycle.set(theTime); - recycle.monthDay ++; - recycle.hour = 0; - recycle.minute = 0; - recycle.second = 0; - return recycle.normalize(true); - } - - public static void setAllowWeekForDetailView(boolean allowWeekView) { - mAllowWeekForDetailView = allowWeekView; - } - - public static boolean getAllowWeekForDetailView() { - return mAllowWeekForDetailView; - } - - public static boolean getConfigBool(Context c, int key) { - return c.getResources().getBoolean(key); - } - - /** - * For devices with Jellybean or later, darkens the given color to ensure that white text is - * clearly visible on top of it. For devices prior to Jellybean, does nothing, as the - * sync adapter handles the color change. - * - * @param color - */ - public static int getDisplayColorFromColor(int color) { - if (!isJellybeanOrLater()) { - return color; - } - - float[] hsv = new float[3]; - Color.colorToHSV(color, hsv); - hsv[1] = Math.min(hsv[1] * SATURATION_ADJUST, 1.0f); - hsv[2] = hsv[2] * INTENSITY_ADJUST; - return Color.HSVToColor(hsv); - } - - // This takes a color and computes what it would look like blended with - // white. The result is the color that should be used for declined events. - public static int getDeclinedColorFromColor(int color) { - int bg = 0xffffffff; - int a = DECLINED_EVENT_ALPHA; - int r = (((color & 0x00ff0000) * a) + ((bg & 0x00ff0000) * (0xff - a))) & 0xff000000; - int g = (((color & 0x0000ff00) * a) + ((bg & 0x0000ff00) * (0xff - a))) & 0x00ff0000; - int b = (((color & 0x000000ff) * a) + ((bg & 0x000000ff) * (0xff - a))) & 0x0000ff00; - return (0xff000000) | ((r | g | b) >> 8); - } - - public static void trySyncAndDisableUpgradeReceiver(Context context) { - final PackageManager pm = context.getPackageManager(); - ComponentName upgradeComponent = new ComponentName(context, UpgradeReceiver.class); - if (pm.getComponentEnabledSetting(upgradeComponent) == - PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { - // The upgrade receiver has been disabled, which means this code has been run before, - // so no need to sync. - return; - } - - Bundle extras = new Bundle(); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); - ContentResolver.requestSync( - null /* no account */, - Calendars.CONTENT_URI.getAuthority(), - extras); - - // Now unregister the receiver so that we won't continue to sync every time. - pm.setComponentEnabledSetting(upgradeComponent, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); - } - - // A single strand represents one color of events. Events are divided up by - // color to make them convenient to draw. The black strand is special in - // that it holds conflicting events as well as color settings for allday on - // each day. - public static class DNAStrand { - public float[] points; - public int[] allDays; // color for the allday, 0 means no event - int position; - public int color; - int count; - } - - // A segment is a single continuous length of time occupied by a single - // color. Segments should never span multiple days. - private static class DNASegment { - int startMinute; // in minutes since the start of the week - int endMinute; - int color; // Calendar color or black for conflicts - int day; // quick reference to the day this segment is on - } - - /** - * Converts a list of events to a list of segments to draw. Assumes list is - * ordered by start time of the events. The function processes events for a - * range of days from firstJulianDay to firstJulianDay + dayXs.length - 1. - * The algorithm goes over all the events and creates a set of segments - * ordered by start time. This list of segments is then converted into a - * HashMap of strands which contain the draw points and are organized by - * color. The strands can then be drawn by setting the paint color to each - * strand's color and calling drawLines on its set of points. The points are - * set up using the following parameters. - *
    - *
  • Events between midnight and WORK_DAY_START_MINUTES are compressed - * into the first 1/8th of the space between top and bottom.
  • - *
  • Events between WORK_DAY_END_MINUTES and the following midnight are - * compressed into the last 1/8th of the space between top and bottom
  • - *
  • Events between WORK_DAY_START_MINUTES and WORK_DAY_END_MINUTES use - * the remaining 3/4ths of the space
  • - *
  • All segments drawn will maintain at least minPixels height, except - * for conflicts in the first or last 1/8th, which may be smaller
  • - *
- * - * @param firstJulianDay The julian day of the first day of events - * @param events A list of events sorted by start time - * @param top The lowest y value the dna should be drawn at - * @param bottom The highest y value the dna should be drawn at - * @param dayXs An array of x values to draw the dna at, one for each day - * @param conflictColor the color to use for conflicts - * @return - */ - public static HashMap createDNAStrands(int firstJulianDay, - ArrayList events, int top, int bottom, int minPixels, int[] dayXs, - Context context) { - - if (!mMinutesLoaded) { - if (context == null) { - Log.wtf(TAG, "No context and haven't loaded parameters yet! Can't create DNA."); - } - Resources res = context.getResources(); - CONFLICT_COLOR = res.getColor(R.color.month_dna_conflict_time_color); - WORK_DAY_START_MINUTES = res.getInteger(R.integer.work_start_minutes); - WORK_DAY_END_MINUTES = res.getInteger(R.integer.work_end_minutes); - WORK_DAY_END_LENGTH = DAY_IN_MINUTES - WORK_DAY_END_MINUTES; - WORK_DAY_MINUTES = WORK_DAY_END_MINUTES - WORK_DAY_START_MINUTES; - mMinutesLoaded = true; - } - - if (events == null || events.isEmpty() || dayXs == null || dayXs.length < 1 - || bottom - top < 8 || minPixels < 0) { - Log.e(TAG, - "Bad values for createDNAStrands! events:" + events + " dayXs:" - + Arrays.toString(dayXs) + " bot-top:" + (bottom - top) + " minPixels:" - + minPixels); - return null; - } - - LinkedList segments = new LinkedList(); - HashMap strands = new HashMap(); - // add a black strand by default, other colors will get added in - // the loop - DNAStrand blackStrand = new DNAStrand(); - blackStrand.color = CONFLICT_COLOR; - strands.put(CONFLICT_COLOR, blackStrand); - // the min length is the number of minutes that will occupy - // MIN_SEGMENT_PIXELS in the 'work day' time slot. This computes the - // minutes/pixel * minpx where the number of pixels are 3/4 the total - // dna height: 4*(mins/(px * 3/4)) - int minMinutes = minPixels * 4 * WORK_DAY_MINUTES / (3 * (bottom - top)); - - // There are slightly fewer than half as many pixels in 1/6 the space, - // so round to 2.5x for the min minutes in the non-work area - int minOtherMinutes = minMinutes * 5 / 2; - int lastJulianDay = firstJulianDay + dayXs.length - 1; - - Event event = new Event(); - // Go through all the events for the week - for (Event currEvent : events) { - // if this event is outside the weeks range skip it - if (currEvent.endDay < firstJulianDay || currEvent.startDay > lastJulianDay) { - continue; - } - if (currEvent.drawAsAllday()) { - addAllDayToStrands(currEvent, strands, firstJulianDay, dayXs.length); - continue; - } - // Copy the event over so we can clip its start and end to our range - currEvent.copyTo(event); - if (event.startDay < firstJulianDay) { - event.startDay = firstJulianDay; - event.startTime = 0; - } - // If it starts after the work day make sure the start is at least - // minPixels from midnight - if (event.startTime > DAY_IN_MINUTES - minOtherMinutes) { - event.startTime = DAY_IN_MINUTES - minOtherMinutes; - } - if (event.endDay > lastJulianDay) { - event.endDay = lastJulianDay; - event.endTime = DAY_IN_MINUTES - 1; - } - // If the end time is before the work day make sure it ends at least - // minPixels after midnight - if (event.endTime < minOtherMinutes) { - event.endTime = minOtherMinutes; - } - // If the start and end are on the same day make sure they are at - // least minPixels apart. This only needs to be done for times - // outside the work day as the min distance for within the work day - // is enforced in the segment code. - if (event.startDay == event.endDay && - event.endTime - event.startTime < minOtherMinutes) { - // If it's less than minPixels in an area before the work - // day - if (event.startTime < WORK_DAY_START_MINUTES) { - // extend the end to the first easy guarantee that it's - // minPixels - event.endTime = Math.min(event.startTime + minOtherMinutes, - WORK_DAY_START_MINUTES + minMinutes); - // if it's in the area after the work day - } else if (event.endTime > WORK_DAY_END_MINUTES) { - // First try shifting the end but not past midnight - event.endTime = Math.min(event.endTime + minOtherMinutes, DAY_IN_MINUTES - 1); - // if it's still too small move the start back - if (event.endTime - event.startTime < minOtherMinutes) { - event.startTime = event.endTime - minOtherMinutes; - } - } - } - - // This handles adding the first segment - if (segments.size() == 0) { - addNewSegment(segments, event, strands, firstJulianDay, 0, minMinutes); - continue; - } - // Now compare our current start time to the end time of the last - // segment in the list - DNASegment lastSegment = segments.getLast(); - int startMinute = (event.startDay - firstJulianDay) * DAY_IN_MINUTES + event.startTime; - int endMinute = Math.max((event.endDay - firstJulianDay) * DAY_IN_MINUTES - + event.endTime, startMinute + minMinutes); - - if (startMinute < 0) { - startMinute = 0; - } - if (endMinute >= WEEK_IN_MINUTES) { - endMinute = WEEK_IN_MINUTES - 1; - } - // If we start before the last segment in the list ends we need to - // start going through the list as this may conflict with other - // events - if (startMinute < lastSegment.endMinute) { - int i = segments.size(); - // find the last segment this event intersects with - while (--i >= 0 && endMinute < segments.get(i).startMinute); - - DNASegment currSegment; - // for each segment this event intersects with - for (; i >= 0 && startMinute <= (currSegment = segments.get(i)).endMinute; i--) { - // if the segment is already a conflict ignore it - if (currSegment.color == CONFLICT_COLOR) { - continue; - } - // if the event ends before the segment and wouldn't create - // a segment that is too small split off the right side - if (endMinute < currSegment.endMinute - minMinutes) { - DNASegment rhs = new DNASegment(); - rhs.endMinute = currSegment.endMinute; - rhs.color = currSegment.color; - rhs.startMinute = endMinute + 1; - rhs.day = currSegment.day; - currSegment.endMinute = endMinute; - segments.add(i + 1, rhs); - strands.get(rhs.color).count++; - if (DEBUG) { - Log.d(TAG, "Added rhs, curr:" + currSegment.toString() + " i:" - + segments.get(i).toString()); - } - } - // if the event starts after the segment and wouldn't create - // a segment that is too small split off the left side - if (startMinute > currSegment.startMinute + minMinutes) { - DNASegment lhs = new DNASegment(); - lhs.startMinute = currSegment.startMinute; - lhs.color = currSegment.color; - lhs.endMinute = startMinute - 1; - lhs.day = currSegment.day; - currSegment.startMinute = startMinute; - // increment i so that we are at the right position when - // referencing the segments to the right and left of the - // current segment. - segments.add(i++, lhs); - strands.get(lhs.color).count++; - if (DEBUG) { - Log.d(TAG, "Added lhs, curr:" + currSegment.toString() + " i:" - + segments.get(i).toString()); - } - } - // if the right side is black merge this with the segment to - // the right if they're on the same day and overlap - if (i + 1 < segments.size()) { - DNASegment rhs = segments.get(i + 1); - if (rhs.color == CONFLICT_COLOR && currSegment.day == rhs.day - && rhs.startMinute <= currSegment.endMinute + 1) { - rhs.startMinute = Math.min(currSegment.startMinute, rhs.startMinute); - segments.remove(currSegment); - strands.get(currSegment.color).count--; - // point at the new current segment - currSegment = rhs; - } - } - // if the left side is black merge this with the segment to - // the left if they're on the same day and overlap - if (i - 1 >= 0) { - DNASegment lhs = segments.get(i - 1); - if (lhs.color == CONFLICT_COLOR && currSegment.day == lhs.day - && lhs.endMinute >= currSegment.startMinute - 1) { - lhs.endMinute = Math.max(currSegment.endMinute, lhs.endMinute); - segments.remove(currSegment); - strands.get(currSegment.color).count--; - // point at the new current segment - currSegment = lhs; - // point i at the new current segment in case new - // code is added - i--; - } - } - // if we're still not black, decrement the count for the - // color being removed, change this to black, and increment - // the black count - if (currSegment.color != CONFLICT_COLOR) { - strands.get(currSegment.color).count--; - currSegment.color = CONFLICT_COLOR; - strands.get(CONFLICT_COLOR).count++; - } - } - - } - // If this event extends beyond the last segment add a new segment - if (endMinute > lastSegment.endMinute) { - addNewSegment(segments, event, strands, firstJulianDay, lastSegment.endMinute, - minMinutes); - } - } - weaveDNAStrands(segments, firstJulianDay, strands, top, bottom, dayXs); - return strands; - } - - // This figures out allDay colors as allDay events are found - private static void addAllDayToStrands(Event event, HashMap strands, - int firstJulianDay, int numDays) { - DNAStrand strand = getOrCreateStrand(strands, CONFLICT_COLOR); - // if we haven't initialized the allDay portion create it now - if (strand.allDays == null) { - strand.allDays = new int[numDays]; - } - - // For each day this event is on update the color - int end = Math.min(event.endDay - firstJulianDay, numDays - 1); - for (int i = Math.max(event.startDay - firstJulianDay, 0); i <= end; i++) { - if (strand.allDays[i] != 0) { - // if this day already had a color, it is now a conflict - strand.allDays[i] = CONFLICT_COLOR; - } else { - // else it's just the color of the event - strand.allDays[i] = event.color; - } - } - } - - // This processes all the segments, sorts them by color, and generates a - // list of points to draw - private static void weaveDNAStrands(LinkedList segments, int firstJulianDay, - HashMap strands, int top, int bottom, int[] dayXs) { - // First, get rid of any colors that ended up with no segments - Iterator strandIterator = strands.values().iterator(); - while (strandIterator.hasNext()) { - DNAStrand strand = strandIterator.next(); - if (strand.count < 1 && strand.allDays == null) { - strandIterator.remove(); - continue; - } - strand.points = new float[strand.count * 4]; - strand.position = 0; - } - // Go through each segment and compute its points - for (DNASegment segment : segments) { - // Add the points to the strand of that color - DNAStrand strand = strands.get(segment.color); - int dayIndex = segment.day - firstJulianDay; - int dayStartMinute = segment.startMinute % DAY_IN_MINUTES; - int dayEndMinute = segment.endMinute % DAY_IN_MINUTES; - int height = bottom - top; - int workDayHeight = height * 3 / 4; - int remainderHeight = (height - workDayHeight) / 2; - - int x = dayXs[dayIndex]; - int y0 = 0; - int y1 = 0; - - y0 = top + getPixelOffsetFromMinutes(dayStartMinute, workDayHeight, remainderHeight); - y1 = top + getPixelOffsetFromMinutes(dayEndMinute, workDayHeight, remainderHeight); - if (DEBUG) { - Log.d(TAG, "Adding " + Integer.toHexString(segment.color) + " at x,y0,y1: " + x - + " " + y0 + " " + y1 + " for " + dayStartMinute + " " + dayEndMinute); - } - strand.points[strand.position++] = x; - strand.points[strand.position++] = y0; - strand.points[strand.position++] = x; - strand.points[strand.position++] = y1; - } - } - - /** - * Compute a pixel offset from the top for a given minute from the work day - * height and the height of the top area. - */ - private static int getPixelOffsetFromMinutes(int minute, int workDayHeight, - int remainderHeight) { - int y; - if (minute < WORK_DAY_START_MINUTES) { - y = minute * remainderHeight / WORK_DAY_START_MINUTES; - } else if (minute < WORK_DAY_END_MINUTES) { - y = remainderHeight + (minute - WORK_DAY_START_MINUTES) * workDayHeight - / WORK_DAY_MINUTES; - } else { - y = remainderHeight + workDayHeight + (minute - WORK_DAY_END_MINUTES) * remainderHeight - / WORK_DAY_END_LENGTH; - } - return y; - } - - /** - * Add a new segment based on the event provided. This will handle splitting - * segments across day boundaries and ensures a minimum size for segments. - */ - private static void addNewSegment(LinkedList segments, Event event, - HashMap strands, int firstJulianDay, int minStart, int minMinutes) { - if (event.startDay > event.endDay) { - Log.wtf(TAG, "Event starts after it ends: " + event.toString()); - } - // If this is a multiday event split it up by day - if (event.startDay != event.endDay) { - Event lhs = new Event(); - lhs.color = event.color; - lhs.startDay = event.startDay; - // the first day we want the start time to be the actual start time - lhs.startTime = event.startTime; - lhs.endDay = lhs.startDay; - lhs.endTime = DAY_IN_MINUTES - 1; - // Nearly recursive iteration! - while (lhs.startDay != event.endDay) { - addNewSegment(segments, lhs, strands, firstJulianDay, minStart, minMinutes); - // The days in between are all day, even though that shouldn't - // actually happen due to the allday filtering - lhs.startDay++; - lhs.endDay = lhs.startDay; - lhs.startTime = 0; - minStart = 0; - } - // The last day we want the end time to be the actual end time - lhs.endTime = event.endTime; - event = lhs; - } - // Create the new segment and compute its fields - DNASegment segment = new DNASegment(); - int dayOffset = (event.startDay - firstJulianDay) * DAY_IN_MINUTES; - int endOfDay = dayOffset + DAY_IN_MINUTES - 1; - // clip the start if needed - segment.startMinute = Math.max(dayOffset + event.startTime, minStart); - // and extend the end if it's too small, but not beyond the end of the - // day - int minEnd = Math.min(segment.startMinute + minMinutes, endOfDay); - segment.endMinute = Math.max(dayOffset + event.endTime, minEnd); - if (segment.endMinute > endOfDay) { - segment.endMinute = endOfDay; - } - - segment.color = event.color; - segment.day = event.startDay; - segments.add(segment); - // increment the count for the correct color or add a new strand if we - // don't have that color yet - DNAStrand strand = getOrCreateStrand(strands, segment.color); - strand.count++; - } - - /** - * Try to get a strand of the given color. Create it if it doesn't exist. - */ - private static DNAStrand getOrCreateStrand(HashMap strands, int color) { - DNAStrand strand = strands.get(color); - if (strand == null) { - strand = new DNAStrand(); - strand.color = color; - strand.count = 0; - strands.put(strand.color, strand); - } - return strand; - } - - /** - * Sends an intent to launch the top level Calendar view. - * - * @param context - */ - public static void returnToCalendarHome(Context context) { - Intent launchIntent = new Intent(context, AllInOneActivity.class); - launchIntent.setAction(Intent.ACTION_DEFAULT); - launchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - launchIntent.putExtra(INTENT_KEY_HOME, true); - context.startActivity(launchIntent); - } - - /** - * Given a context and a time in millis since unix epoch figures out the - * correct week of the year for that time. - * - * @param millisSinceEpoch - * @return - */ - public static int getWeekNumberFromTime(long millisSinceEpoch, Context context) { - Time weekTime = new Time(getTimeZone(context, null)); - weekTime.set(millisSinceEpoch); - weekTime.normalize(true); - int firstDayOfWeek = getFirstDayOfWeek(context); - // if the date is on Saturday or Sunday and the start of the week - // isn't Monday we may need to shift the date to be in the correct - // week - if (weekTime.weekDay == Time.SUNDAY - && (firstDayOfWeek == Time.SUNDAY || firstDayOfWeek == Time.SATURDAY)) { - weekTime.monthDay++; - weekTime.normalize(true); - } else if (weekTime.weekDay == Time.SATURDAY && firstDayOfWeek == Time.SATURDAY) { - weekTime.monthDay += 2; - weekTime.normalize(true); - } - return weekTime.getWeekNumber(); - } - - /** - * Formats a day of the week string. This is either just the name of the day - * or a combination of yesterday/today/tomorrow and the day of the week. - * - * @param julianDay The julian day to get the string for - * @param todayJulianDay The julian day for today's date - * @param millis A utc millis since epoch time that falls on julian day - * @param context The calling context, used to get the timezone and do the - * formatting - * @return - */ - public static String getDayOfWeekString(int julianDay, int todayJulianDay, long millis, - Context context) { - getTimeZone(context, null); - int flags = DateUtils.FORMAT_SHOW_WEEKDAY; - String dayViewText; - if (julianDay == todayJulianDay) { - dayViewText = context.getString(R.string.agenda_today, - mTZUtils.formatDateRange(context, millis, millis, flags).toString()); - } else if (julianDay == todayJulianDay - 1) { - dayViewText = context.getString(R.string.agenda_yesterday, - mTZUtils.formatDateRange(context, millis, millis, flags).toString()); - } else if (julianDay == todayJulianDay + 1) { - dayViewText = context.getString(R.string.agenda_tomorrow, - mTZUtils.formatDateRange(context, millis, millis, flags).toString()); - } else { - dayViewText = mTZUtils.formatDateRange(context, millis, millis, flags).toString(); - } - dayViewText = dayViewText.toUpperCase(); - return dayViewText; - } - - // Calculate the time until midnight + 1 second and set the handler to - // do run the runnable - public static void setMidnightUpdater(Handler h, Runnable r, String timezone) { - if (h == null || r == null || timezone == null) { - return; - } - long now = System.currentTimeMillis(); - Time time = new Time(timezone); - time.set(now); - long runInMillis = (24 * 3600 - time.hour * 3600 - time.minute * 60 - - time.second + 1) * 1000; - h.removeCallbacks(r); - h.postDelayed(r, runInMillis); - } - - // Stop the midnight update thread - public static void resetMidnightUpdater(Handler h, Runnable r) { - if (h == null || r == null) { - return; - } - h.removeCallbacks(r); - } - - /** - * Returns a string description of the specified time interval. - */ - public static String getDisplayedDatetime(long startMillis, long endMillis, long currentMillis, - String localTimezone, boolean allDay, Context context) { - // Configure date/time formatting. - int flagsDate = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; - int flagsTime = DateUtils.FORMAT_SHOW_TIME; - if (DateFormat.is24HourFormat(context)) { - flagsTime |= DateUtils.FORMAT_24HOUR; - } - - Time currentTime = new Time(localTimezone); - currentTime.set(currentMillis); - Resources resources = context.getResources(); - String datetimeString = null; - if (allDay) { - // All day events require special timezone adjustment. - long localStartMillis = convertAlldayUtcToLocal(null, startMillis, localTimezone); - long localEndMillis = convertAlldayUtcToLocal(null, endMillis, localTimezone); - if (singleDayEvent(localStartMillis, localEndMillis, currentTime.gmtoff)) { - // If possible, use "Today" or "Tomorrow" instead of a full date string. - int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), - localStartMillis, currentMillis, currentTime.gmtoff); - if (TODAY == todayOrTomorrow) { - datetimeString = resources.getString(R.string.today); - } else if (TOMORROW == todayOrTomorrow) { - datetimeString = resources.getString(R.string.tomorrow); - } - } - if (datetimeString == null) { - // For multi-day allday events or single-day all-day events that are not - // today or tomorrow, use framework formatter. - Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault()); - datetimeString = DateUtils.formatDateRange(context, f, startMillis, - endMillis, flagsDate, Time.TIMEZONE_UTC).toString(); - } - } else { - if (singleDayEvent(startMillis, endMillis, currentTime.gmtoff)) { - // Format the time. - String timeString = Utils.formatDateRange(context, startMillis, endMillis, - flagsTime); - - // If possible, use "Today" or "Tomorrow" instead of a full date string. - int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), startMillis, - currentMillis, currentTime.gmtoff); - if (TODAY == todayOrTomorrow) { - // Example: "Today at 1:00pm - 2:00 pm" - datetimeString = resources.getString(R.string.today_at_time_fmt, - timeString); - } else if (TOMORROW == todayOrTomorrow) { - // Example: "Tomorrow at 1:00pm - 2:00 pm" - datetimeString = resources.getString(R.string.tomorrow_at_time_fmt, - timeString); - } else { - // Format the full date. Example: "Thursday, April 12, 1:00pm - 2:00pm" - String dateString = Utils.formatDateRange(context, startMillis, endMillis, - flagsDate); - datetimeString = resources.getString(R.string.date_time_fmt, dateString, - timeString); - } - } else { - // For multiday events, shorten day/month names. - // Example format: "Fri Apr 6, 5:00pm - Sun, Apr 8, 6:00pm" - int flagsDatetime = flagsDate | flagsTime | DateUtils.FORMAT_ABBREV_MONTH | - DateUtils.FORMAT_ABBREV_WEEKDAY; - datetimeString = Utils.formatDateRange(context, startMillis, endMillis, - flagsDatetime); - } - } - return datetimeString; - } - - /** - * Returns the timezone to display in the event info, if the local timezone is different - * from the event timezone. Otherwise returns null. - */ - public static String getDisplayedTimezone(long startMillis, String localTimezone, - String eventTimezone) { - String tzDisplay = null; - if (!TextUtils.equals(localTimezone, eventTimezone)) { - // Figure out if this is in DST - TimeZone tz = TimeZone.getTimeZone(localTimezone); - if (tz == null || tz.getID().equals("GMT")) { - tzDisplay = localTimezone; - } else { - Time startTime = new Time(localTimezone); - startTime.set(startMillis); - tzDisplay = tz.getDisplayName(startTime.isDst != 0, TimeZone.SHORT); - } - } - return tzDisplay; - } - - /** - * Returns whether the specified time interval is in a single day. - */ - private static boolean singleDayEvent(long startMillis, long endMillis, long localGmtOffset) { - if (startMillis == endMillis) { - return true; - } - - // An event ending at midnight should still be a single-day event, so check - // time end-1. - int startDay = Time.getJulianDay(startMillis, localGmtOffset); - int endDay = Time.getJulianDay(endMillis - 1, localGmtOffset); - return startDay == endDay; - } - - // Using int constants as a return value instead of an enum to minimize resources. - private static final int TODAY = 1; - private static final int TOMORROW = 2; - private static final int NONE = 0; - - /** - * Returns TODAY or TOMORROW if applicable. Otherwise returns NONE. - */ - private static int isTodayOrTomorrow(Resources r, long dayMillis, - long currentMillis, long localGmtOffset) { - int startDay = Time.getJulianDay(dayMillis, localGmtOffset); - int currentDay = Time.getJulianDay(currentMillis, localGmtOffset); - - int days = startDay - currentDay; - if (days == 1) { - return TOMORROW; - } else if (days == 0) { - return TODAY; - } else { - return NONE; - } - } - - /** - * Inserts a drawable with today's day into the today's icon in the option menu - * @param icon - today's icon from the options menu - */ - public static void setTodayIcon(LayerDrawable icon, Context c, String timezone) { - DayOfMonthDrawable today; - - // Reuse current drawable if possible - Drawable currentDrawable = icon.findDrawableByLayerId(R.id.today_icon_day); - if (currentDrawable != null && currentDrawable instanceof DayOfMonthDrawable) { - today = (DayOfMonthDrawable)currentDrawable; - } else { - today = new DayOfMonthDrawable(c); - } - // Set the day and update the icon - Time now = new Time(timezone); - now.setToNow(); - now.normalize(false); - today.setDayOfMonth(now.monthDay); - icon.mutate(); - icon.setDrawableByLayerId(R.id.today_icon_day, today); - } - - /** - * Get a list of quick responses used for emailing guests from the - * SharedPreferences. If not are found, get the hard coded ones that shipped - * with the app - * - * @param context - * @return a list of quick responses. - */ - public static String[] getQuickResponses(Context context) { - String[] s = Utils.getSharedPreference(context, KEY_QUICK_RESPONSES, (String[]) null); - - if (s == null) { - s = context.getResources().getStringArray(R.array.quick_response_defaults); - } - - return s; - } - - /** - * Return the app version code. - */ - public static String getVersionCode(Context context) { - if (sVersion == null) { - try { - sVersion = context.getPackageManager().getPackageInfo( - context.getPackageName(), 0).versionName; - } catch (PackageManager.NameNotFoundException e) { - // Can't find version; just leave it blank. - Log.e(TAG, "Error finding package " + context.getApplicationInfo().packageName); - } - } - return sVersion; - } -} diff --git a/src/com/android/calendar/alerts/AlarmManagerInterface.java b/src/com/android/calendar/alerts/AlarmManagerInterface.java deleted file mode 100644 index 3c66434d..00000000 --- a/src/com/android/calendar/alerts/AlarmManagerInterface.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2012 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.alerts; - -import android.app.PendingIntent; - -/** - * AlarmManager abstracted to an interface for testability. - */ -public interface AlarmManagerInterface { - public void set(int type, long triggerAtMillis, PendingIntent operation); -} diff --git a/src/com/android/calendar/alerts/AlarmScheduler.java b/src/com/android/calendar/alerts/AlarmScheduler.java deleted file mode 100644 index 97828229..00000000 --- a/src/com/android/calendar/alerts/AlarmScheduler.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright (C) 2012 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.alerts; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.provider.CalendarContract; -import android.provider.CalendarContract.Events; -import android.provider.CalendarContract.Instances; -import android.provider.CalendarContract.Reminders; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; - -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Schedules the next EVENT_REMINDER_APP broadcast with AlarmManager, by querying the events - * and reminders tables for the next upcoming alert. - */ -public class AlarmScheduler { - private static final String TAG = "AlarmScheduler"; - - private static final String INSTANCES_WHERE = Events.VISIBLE + "=? AND " - + Instances.BEGIN + ">=? AND " + Instances.BEGIN + "<=? AND " - + Events.ALL_DAY + "=?"; - static final String[] INSTANCES_PROJECTION = new String[] { - Instances.EVENT_ID, - Instances.BEGIN, - Instances.ALL_DAY, - }; - private static final int INSTANCES_INDEX_EVENTID = 0; - private static final int INSTANCES_INDEX_BEGIN = 1; - private static final int INSTANCES_INDEX_ALL_DAY = 2; - - private static final String REMINDERS_WHERE = Reminders.METHOD + "=1 AND " - + Reminders.EVENT_ID + " IN "; - static final String[] REMINDERS_PROJECTION = new String[] { - Reminders.EVENT_ID, - Reminders.MINUTES, - Reminders.METHOD, - }; - private static final int REMINDERS_INDEX_EVENT_ID = 0; - private static final int REMINDERS_INDEX_MINUTES = 1; - private static final int REMINDERS_INDEX_METHOD = 2; - - // Add a slight delay for the EVENT_REMINDER_APP broadcast for a couple reasons: - // (1) so that the concurrent reminder broadcast from the provider doesn't result - // in a double ring, and (2) some OEMs modified the provider to not add an alert to - // the CalendarAlerts table until the alert time, so for the unbundled app's - // notifications to work on these devices, a delay ensures that AlertService won't - // read from the CalendarAlerts table until the alert is present. - static final int ALARM_DELAY_MS = 1000; - - // The reminders query looks like "SELECT ... AND eventId IN 101,102,202,...". This - // sets the max # of events in the query before batching into multiple queries, to - // limit the SQL query length. - private static final int REMINDER_QUERY_BATCH_SIZE = 50; - - // We really need to query for reminder times that fall in some interval, but - // the Reminders table only stores the reminder interval (10min, 15min, etc), and - // we cannot do the join with the Events table to calculate the actual alert time - // from outside of the provider. So the best we can do for now consider events - // whose start times begin within some interval (ie. 1 week out). This means - // reminders which are configured for more than 1 week out won't fire on time. We - // can minimize this to being only 1 day late by putting a 1 day max on the alarm time. - private static final long EVENT_LOOKAHEAD_WINDOW_MS = DateUtils.WEEK_IN_MILLIS; - private static final long MAX_ALARM_ELAPSED_MS = DateUtils.DAY_IN_MILLIS; - - /** - * Schedules the nearest upcoming alarm, to refresh notifications. - * - * This is historically done in the provider but we dupe this here so the unbundled - * app will work on devices that have modified this portion of the provider. This - * has the limitation of querying events within some interval from now (ie. looks at - * reminders for all events occurring in the next week). This means for example, - * a 2 week notification will not fire on time. - */ - public static void scheduleNextAlarm(Context context) { - scheduleNextAlarm(context, AlertUtils.createAlarmManager(context), - REMINDER_QUERY_BATCH_SIZE, System.currentTimeMillis()); - } - - // VisibleForTesting - static void scheduleNextAlarm(Context context, AlarmManagerInterface alarmManager, - int batchSize, long currentMillis) { - Cursor instancesCursor = null; - try { - instancesCursor = queryUpcomingEvents(context, context.getContentResolver(), - currentMillis); - if (instancesCursor != null) { - queryNextReminderAndSchedule(instancesCursor, context, - context.getContentResolver(), alarmManager, batchSize, currentMillis); - } - } finally { - if (instancesCursor != null) { - instancesCursor.close(); - } - } - } - - /** - * Queries events starting within a fixed interval from now. - */ - private static Cursor queryUpcomingEvents(Context context, ContentResolver contentResolver, - long currentMillis) { - Time time = new Time(); - time.normalize(false); - long localOffset = time.gmtoff * 1000; - final long localStartMin = currentMillis; - final long localStartMax = localStartMin + EVENT_LOOKAHEAD_WINDOW_MS; - final long utcStartMin = localStartMin - localOffset; - final long utcStartMax = utcStartMin + EVENT_LOOKAHEAD_WINDOW_MS; - - // Expand Instances table range by a day on either end to account for - // all-day events. - Uri.Builder uriBuilder = Instances.CONTENT_URI.buildUpon(); - ContentUris.appendId(uriBuilder, localStartMin - DateUtils.DAY_IN_MILLIS); - ContentUris.appendId(uriBuilder, localStartMax + DateUtils.DAY_IN_MILLIS); - - // Build query for all events starting within the fixed interval. - StringBuilder queryBuilder = new StringBuilder(); - queryBuilder.append("("); - queryBuilder.append(INSTANCES_WHERE); - queryBuilder.append(") OR ("); - queryBuilder.append(INSTANCES_WHERE); - queryBuilder.append(")"); - String[] queryArgs = new String[] { - // allday selection - "1", /* visible = ? */ - String.valueOf(utcStartMin), /* begin >= ? */ - String.valueOf(utcStartMax), /* begin <= ? */ - "1", /* allDay = ? */ - - // non-allday selection - "1", /* visible = ? */ - String.valueOf(localStartMin), /* begin >= ? */ - String.valueOf(localStartMax), /* begin <= ? */ - "0" /* allDay = ? */ - }; - - Cursor cursor = contentResolver.query(uriBuilder.build(), INSTANCES_PROJECTION, - queryBuilder.toString(), queryArgs, null); - return cursor; - } - - /** - * Queries for all the reminders of the events in the instancesCursor, and schedules - * the alarm for the next upcoming reminder. - */ - private static void queryNextReminderAndSchedule(Cursor instancesCursor, Context context, - ContentResolver contentResolver, AlarmManagerInterface alarmManager, - int batchSize, long currentMillis) { - if (AlertService.DEBUG) { - int eventCount = instancesCursor.getCount(); - if (eventCount == 0) { - Log.d(TAG, "No events found starting within 1 week."); - } else { - Log.d(TAG, "Query result count for events starting within 1 week: " + eventCount); - } - } - - // Put query results of all events starting within some interval into map of event ID to - // local start time. - Map> eventMap = new HashMap>(); - Time timeObj = new Time(); - long nextAlarmTime = Long.MAX_VALUE; - int nextAlarmEventId = 0; - instancesCursor.moveToPosition(-1); - while (!instancesCursor.isAfterLast()) { - int index = 0; - eventMap.clear(); - StringBuilder eventIdsForQuery = new StringBuilder(); - eventIdsForQuery.append('('); - while (index++ < batchSize && instancesCursor.moveToNext()) { - int eventId = instancesCursor.getInt(INSTANCES_INDEX_EVENTID); - long begin = instancesCursor.getLong(INSTANCES_INDEX_BEGIN); - boolean allday = instancesCursor.getInt(INSTANCES_INDEX_ALL_DAY) != 0; - long localStartTime; - if (allday) { - // Adjust allday to local time. - localStartTime = Utils.convertAlldayUtcToLocal(timeObj, begin, - Time.getCurrentTimezone()); - } else { - localStartTime = begin; - } - List startTimes = eventMap.get(eventId); - if (startTimes == null) { - startTimes = new ArrayList(); - eventMap.put(eventId, startTimes); - eventIdsForQuery.append(eventId); - eventIdsForQuery.append(","); - } - startTimes.add(localStartTime); - - // Log for debugging. - if (Log.isLoggable(TAG, Log.DEBUG)) { - timeObj.set(localStartTime); - StringBuilder msg = new StringBuilder(); - msg.append("Events cursor result -- eventId:").append(eventId); - msg.append(", allDay:").append(allday); - msg.append(", start:").append(localStartTime); - msg.append(" (").append(timeObj.format("%a, %b %d, %Y %I:%M%P")).append(")"); - Log.d(TAG, msg.toString()); - } - } - if (eventIdsForQuery.charAt(eventIdsForQuery.length() - 1) == ',') { - eventIdsForQuery.deleteCharAt(eventIdsForQuery.length() - 1); - } - eventIdsForQuery.append(')'); - - // Query the reminders table for the events found. - Cursor cursor = null; - try { - cursor = contentResolver.query(Reminders.CONTENT_URI, REMINDERS_PROJECTION, - REMINDERS_WHERE + eventIdsForQuery, null, null); - - // Process the reminders query results to find the next reminder time. - cursor.moveToPosition(-1); - while (cursor.moveToNext()) { - int eventId = cursor.getInt(REMINDERS_INDEX_EVENT_ID); - int reminderMinutes = cursor.getInt(REMINDERS_INDEX_MINUTES); - List startTimes = eventMap.get(eventId); - if (startTimes != null) { - for (Long startTime : startTimes) { - long alarmTime = startTime - - reminderMinutes * DateUtils.MINUTE_IN_MILLIS; - if (alarmTime > currentMillis && alarmTime < nextAlarmTime) { - nextAlarmTime = alarmTime; - nextAlarmEventId = eventId; - } - - if (Log.isLoggable(TAG, Log.DEBUG)) { - timeObj.set(alarmTime); - StringBuilder msg = new StringBuilder(); - msg.append("Reminders cursor result -- eventId:").append(eventId); - msg.append(", startTime:").append(startTime); - msg.append(", minutes:").append(reminderMinutes); - msg.append(", alarmTime:").append(alarmTime); - msg.append(" (").append(timeObj.format("%a, %b %d, %Y %I:%M%P")) - .append(")"); - Log.d(TAG, msg.toString()); - } - } - } - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - } - - // Schedule the alarm for the next reminder time. - if (nextAlarmTime < Long.MAX_VALUE) { - scheduleAlarm(context, nextAlarmEventId, nextAlarmTime, currentMillis, alarmManager); - } - } - - /** - * Schedules an alarm for the EVENT_REMINDER_APP broadcast, for the specified - * alarm time with a slight delay (to account for the possible duplicate broadcast - * from the provider). - */ - private static void scheduleAlarm(Context context, long eventId, long alarmTime, - long currentMillis, AlarmManagerInterface alarmManager) { - // Max out the alarm time to 1 day out, so an alert for an event far in the future - // (not present in our event query results for a limited range) can only be at - // most 1 day late. - long maxAlarmTime = currentMillis + MAX_ALARM_ELAPSED_MS; - if (alarmTime > maxAlarmTime) { - alarmTime = maxAlarmTime; - } - - // Add a slight delay (see comments on the member var). - alarmTime += ALARM_DELAY_MS; - - if (AlertService.DEBUG) { - Time time = new Time(); - time.set(alarmTime); - String schedTime = time.format("%a, %b %d, %Y %I:%M%P"); - Log.d(TAG, "Scheduling alarm for EVENT_REMINDER_APP broadcast for event " + eventId - + " at " + alarmTime + " (" + schedTime + ")"); - } - - // Schedule an EVENT_REMINDER_APP broadcast with AlarmManager. The extra is - // only used by AlertService for logging. It is ignored by Intent.filterEquals, - // so this scheduling will still overwrite the alarm that was previously pending. - // Note that the 'setClass' is required, because otherwise it seems the broadcast - // can be eaten by other apps and we somehow may never receive it. - Intent intent = new Intent(AlertReceiver.EVENT_REMINDER_APP_ACTION); - intent.setClass(context, AlertReceiver.class); - intent.putExtra(CalendarContract.CalendarAlerts.ALARM_TIME, alarmTime); - PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0); - alarmManager.set(AlarmManager.RTC_WAKEUP, alarmTime, pi); - } -} diff --git a/src/com/android/calendar/alerts/AlertReceiver.java b/src/com/android/calendar/alerts/AlertReceiver.java deleted file mode 100644 index ce80cae1..00000000 --- a/src/com/android/calendar/alerts/AlertReceiver.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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.alerts; - -import android.app.Notification; -import android.app.PendingIntent; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.PowerManager; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.telephony.TelephonyManager; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.text.style.RelativeSizeSpan; -import android.text.style.TextAppearanceSpan; -import android.text.style.URLSpan; -import android.util.Log; -import android.view.View; -import android.widget.RemoteViews; - -import com.android.calendar.R; -import com.android.calendar.Utils; -import com.android.calendar.alerts.AlertService.NotificationWrapper; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -/** - * Receives android.intent.action.EVENT_REMINDER intents and handles - * event reminders. The intent URI specifies an alert id in the - * CalendarAlerts database table. This class also receives the - * BOOT_COMPLETED intent so that it can add a status bar notification - * if there are Calendar event alarms that have not been dismissed. - * It also receives the TIME_CHANGED action so that it can fire off - * snoozed alarms that have become ready. The real work is done in - * the AlertService class. - * - * To trigger this code after pushing the apk to device: - * adb shell am broadcast -a "android.intent.action.EVENT_REMINDER" - * -n "com.android.calendar/.alerts.AlertReceiver" - */ -public class AlertReceiver extends BroadcastReceiver { - private static final String TAG = "AlertReceiver"; - - // The broadcast for notification refreshes scheduled by the app. This is to - // distinguish the EVENT_REMINDER broadcast sent by the provider. - public static final String EVENT_REMINDER_APP_ACTION = - "com.android.calendar.EVENT_REMINDER_APP"; - - public static final String ACTION_DISMISS_OLD_REMINDERS = "removeOldReminders"; - - @Override - public void onReceive(final Context context, final Intent intent) { - if (AlertService.DEBUG) { - Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); - } - closeNotificationShade(context); - } - - public static NotificationWrapper makeBasicNotification(Context context, String title, - String summaryText, long startMillis, long endMillis, long eventId, - int notificationId, boolean doPopup, int priority) { - Notification n = buildBasicNotification(new Notification.Builder(context), - context, title, summaryText, startMillis, endMillis, eventId, notificationId, - doPopup, priority, false); - return new NotificationWrapper(n, notificationId, eventId, startMillis, endMillis, doPopup); - } - - private static Notification buildBasicNotification(Notification.Builder notificationBuilder, - Context context, String title, String summaryText, long startMillis, long endMillis, - long eventId, int notificationId, boolean doPopup, int priority, - boolean addActionButtons) { - Resources resources = context.getResources(); - if (title == null || title.length() == 0) { - title = resources.getString(R.string.no_title_label); - } - - // Create the base notification. - notificationBuilder.setContentTitle(title); - notificationBuilder.setContentText(summaryText); - notificationBuilder.setSmallIcon(R.drawable.stat_notify_calendar); - if (Utils.isJellybeanOrLater()) { - // Turn off timestamp. - notificationBuilder.setWhen(0); - - // Should be one of the values in Notification (ie. Notification.PRIORITY_HIGH, etc). - // A higher priority will encourage notification manager to expand it. - notificationBuilder.setPriority(priority); - } - return notificationBuilder.getNotification(); - } - - private void closeNotificationShade(Context context) { - Intent closeNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - context.sendBroadcast(closeNotificationShadeIntent); - } -} diff --git a/src/com/android/calendar/alerts/AlertService.java b/src/com/android/calendar/alerts/AlertService.java deleted file mode 100644 index d2c994da..00000000 --- a/src/com/android/calendar/alerts/AlertService.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2008 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.alerts; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.Service; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.Process; -import android.provider.CalendarContract; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.CalendarAlerts; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.TimeZone; - -/** - * This service is used to handle calendar event reminders. - */ -public class AlertService extends Service { - static final boolean DEBUG = true; - private static final String TAG = "AlertService"; - - private volatile Looper mServiceLooper; - - static final String[] ALERT_PROJECTION = new String[] { - CalendarAlerts._ID, // 0 - CalendarAlerts.EVENT_ID, // 1 - CalendarAlerts.STATE, // 2 - CalendarAlerts.TITLE, // 3 - CalendarAlerts.EVENT_LOCATION, // 4 - CalendarAlerts.SELF_ATTENDEE_STATUS, // 5 - CalendarAlerts.ALL_DAY, // 6 - CalendarAlerts.ALARM_TIME, // 7 - CalendarAlerts.MINUTES, // 8 - CalendarAlerts.BEGIN, // 9 - CalendarAlerts.END, // 10 - CalendarAlerts.DESCRIPTION, // 11 - }; - - 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; - private static final int ALERT_INDEX_TITLE = 3; - private static final int ALERT_INDEX_EVENT_LOCATION = 4; - private static final int ALERT_INDEX_SELF_ATTENDEE_STATUS = 5; - private static final int ALERT_INDEX_ALL_DAY = 6; - private static final int ALERT_INDEX_ALARM_TIME = 7; - private static final int ALERT_INDEX_MINUTES = 8; - private static final int ALERT_INDEX_BEGIN = 9; - private static final int ALERT_INDEX_END = 10; - private static final int ALERT_INDEX_DESCRIPTION = 11; - - private static final String ACTIVE_ALERTS_SELECTION = "(" + CalendarAlerts.STATE + "=? OR " - + CalendarAlerts.STATE + "=?) AND " + CalendarAlerts.ALARM_TIME + "<="; - - private static final String[] ACTIVE_ALERTS_SELECTION_ARGS = new String[] { - Integer.toString(CalendarAlerts.STATE_FIRED), - Integer.toString(CalendarAlerts.STATE_SCHEDULED) - }; - - private static final String ACTIVE_ALERTS_SORT = "begin DESC, end DESC"; - - private static final String DISMISS_OLD_SELECTION = CalendarAlerts.END + " mNw; - - public NotificationWrapper(Notification n, int notificationId, long eventId, - long startMillis, long endMillis, boolean doPopup) { - mNotification = n; - mEventId = eventId; - mBegin = startMillis; - mEnd = endMillis; - - // popup? - // notification id? - } - - public NotificationWrapper(Notification n) { - mNotification = n; - } - - public void add(NotificationWrapper nw) { - if (mNw == null) { - mNw = new ArrayList(); - } - mNw.add(nw); - } - } - - // Added wrapper for testing - public static class NotificationMgrWrapper extends NotificationMgr { - NotificationManager mNm; - - public NotificationMgrWrapper(NotificationManager nm) { - mNm = nm; - } - - @Override - public void cancel(int id) { - mNm.cancel(id); - } - - @Override - public void notify(int id, NotificationWrapper nw) { - mNm.notify(id, nw.mNotification); - } - } - - static class NotificationInfo { - String eventName; - String location; - String description; - long startMillis; - long endMillis; - long eventId; - boolean allDay; - boolean newAlert; - - NotificationInfo(String eventName, String location, String description, long startMillis, - long endMillis, long eventId, boolean allDay, boolean newAlert) { - this.eventName = eventName; - this.location = location; - this.description = description; - this.startMillis = startMillis; - this.endMillis = endMillis; - this.eventId = eventId; - this.newAlert = newAlert; - this.allDay = allDay; - } - } - - @Override - public void onCreate() { - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - return START_REDELIVER_INTENT; - } - - @Override - public void onDestroy() { - mServiceLooper.quit(); - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } -} diff --git a/src/com/android/calendar/alerts/AlertUtils.java b/src/com/android/calendar/alerts/AlertUtils.java deleted file mode 100644 index b9aaec29..00000000 --- a/src/com/android/calendar/alerts/AlertUtils.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2012 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.alerts; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.Uri; -import android.provider.CalendarContract; -import android.provider.CalendarContract.CalendarAlerts; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; - -import com.android.calendar.EventInfoActivity; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; - -public class AlertUtils { - private static final String TAG = "AlertUtils"; - static final boolean DEBUG = true; - - public static final long SNOOZE_DELAY = 5 * 60 * 1000L; - - // We use one notification id for the expired events notification. All - // other notifications (the 'active' future/concurrent ones) use a unique ID. - public static final int EXPIRED_GROUP_NOTIFICATION_ID = 0; - - public static final String EVENT_ID_KEY = "eventid"; - public static final String EVENT_START_KEY = "eventstart"; - public static final String EVENT_END_KEY = "eventend"; - public static final String NOTIFICATION_ID_KEY = "notificationid"; - public static final String EVENT_IDS_KEY = "eventids"; - public static final String EVENT_STARTS_KEY = "starts"; - - // A flag for using local storage to save alert state instead of the alerts DB table. - // This allows the unbundled app to run alongside other calendar apps without eating - // alerts from other apps. - static boolean BYPASS_DB = true; - - /** - * Creates an AlarmManagerInterface that wraps a real AlarmManager. The alarm code - * was abstracted to an interface to make it testable. - */ - public static AlarmManagerInterface createAlarmManager(Context context) { - final AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - return new AlarmManagerInterface() { - @Override - public void set(int type, long triggerAtMillis, PendingIntent operation) { - if (Utils.isKeyLimePieOrLater()) { - mgr.setExact(type, triggerAtMillis, operation); - } else { - mgr.set(type, triggerAtMillis, operation); - } - } - }; - } - - /** - * Schedules an alarm intent with the system AlarmManager that will notify - * listeners when a reminder should be fired. The provider will keep - * scheduled reminders up to date but apps may use this to implement snooze - * functionality without modifying the reminders table. Scheduled alarms - * will generate an intent using AlertReceiver.EVENT_REMINDER_APP_ACTION. - * - * @param context A context for referencing system resources - * @param manager The AlarmManager to use or null - * @param alarmTime The time to fire the intent in UTC millis since epoch - */ - public static void scheduleAlarm(Context context, AlarmManagerInterface manager, - long alarmTime) { - } - - public static Intent buildEventViewIntent(Context c, long eventId, long begin, long end) { - Intent i = new Intent(Intent.ACTION_VIEW); - Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon(); - builder.appendEncodedPath("events/" + eventId); - i.setData(builder.build()); - i.setClass(c, EventInfoActivity.class); - i.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, begin); - i.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end); - return i; - } -} diff --git a/src/com/android/calendar/alerts/DismissAlarmsService.java b/src/com/android/calendar/alerts/DismissAlarmsService.java deleted file mode 100644 index 1ec3c22d..00000000 --- a/src/com/android/calendar/alerts/DismissAlarmsService.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.alerts; - -import android.app.IntentService; -import android.app.NotificationManager; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.IBinder; -import android.provider.CalendarContract.CalendarAlerts; -import androidx.core.app.TaskStackBuilder; - -import android.util.Log; -import com.android.calendar.EventInfoActivity; -import com.android.calendar.alerts.GlobalDismissManager.AlarmId; - -import java.util.LinkedList; -import java.util.List; - -/** - * Service for asynchronously marking fired alarms as dismissed. - */ -public class DismissAlarmsService extends IntentService { - private static final String TAG = "DismissAlarmsService"; - public static final String SHOW_ACTION = "com.android.calendar.SHOW"; - public static final String DISMISS_ACTION = "com.android.calendar.DISMISS"; - - private static final String[] PROJECTION = new String[] { - CalendarAlerts.STATE, - }; - private static final int COLUMN_INDEX_STATE = 0; - - public DismissAlarmsService() { - super("DismissAlarmsService"); - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public void onHandleIntent(Intent intent) { - if (AlertService.DEBUG) { - Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); - } - - long eventId = intent.getLongExtra(AlertUtils.EVENT_ID_KEY, -1); - long eventStart = intent.getLongExtra(AlertUtils.EVENT_START_KEY, -1); - long eventEnd = intent.getLongExtra(AlertUtils.EVENT_END_KEY, -1); - long[] eventIds = intent.getLongArrayExtra(AlertUtils.EVENT_IDS_KEY); - long[] eventStarts = intent.getLongArrayExtra(AlertUtils.EVENT_STARTS_KEY); - int notificationId = intent.getIntExtra(AlertUtils.NOTIFICATION_ID_KEY, -1); - List alarmIds = new LinkedList(); - - Uri uri = CalendarAlerts.CONTENT_URI; - String selection; - - // Dismiss a specific fired alarm if id is present, otherwise, dismiss all alarms - if (eventId != -1) { - alarmIds.add(new AlarmId(eventId, eventStart)); - selection = CalendarAlerts.STATE + "=" + CalendarAlerts.STATE_FIRED + " AND " + - CalendarAlerts.EVENT_ID + "=" + eventId; - } else if (eventIds != null && eventIds.length > 0 && - eventStarts != null && eventIds.length == eventStarts.length) { - selection = buildMultipleEventsQuery(eventIds); - for (int i = 0; i < eventIds.length; i++) { - alarmIds.add(new AlarmId(eventIds[i], eventStarts[i])); - } - } else { - // NOTE: I don't believe that this ever happens. - selection = CalendarAlerts.STATE + "=" + CalendarAlerts.STATE_FIRED; - } - - GlobalDismissManager.dismissGlobally(getApplicationContext(), alarmIds); - - ContentResolver resolver = getContentResolver(); - ContentValues values = new ContentValues(); - values.put(PROJECTION[COLUMN_INDEX_STATE], CalendarAlerts.STATE_DISMISSED); - resolver.update(uri, values, selection, null); - - // Remove from notification bar. - if (notificationId != -1) { - NotificationManager nm = - (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - nm.cancel(notificationId); - } - - if (SHOW_ACTION.equals(intent.getAction())) { - // Show event on Calendar app by building an intent and task stack to start - // EventInfoActivity with AllInOneActivity as the parent activity rooted to home. - Intent i = AlertUtils.buildEventViewIntent(this, eventId, eventStart, eventEnd); - - TaskStackBuilder.create(this) - .addParentStack(EventInfoActivity.class).addNextIntent(i).startActivities(); - } - } - - private String buildMultipleEventsQuery(long[] eventIds) { - StringBuilder selection = new StringBuilder(); - selection.append(CalendarAlerts.STATE); - selection.append("="); - selection.append(CalendarAlerts.STATE_FIRED); - if (eventIds.length > 0) { - selection.append(" AND ("); - selection.append(CalendarAlerts.EVENT_ID); - selection.append("="); - selection.append(eventIds[0]); - for (int i = 1; i < eventIds.length; i++) { - selection.append(" OR "); - selection.append(CalendarAlerts.EVENT_ID); - selection.append("="); - selection.append(eventIds[i]); - } - selection.append(")"); - } - return selection.toString(); - } -} diff --git a/src/com/android/calendar/alerts/GlobalDismissManager.java b/src/com/android/calendar/alerts/GlobalDismissManager.java deleted file mode 100644 index 27b3e162..00000000 --- a/src/com/android/calendar/alerts/GlobalDismissManager.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.alerts; - -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.provider.CalendarContract.CalendarAlerts; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.util.Log; -import android.util.Pair; - -import com.android.calendar.R; - -import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Utilities for managing notification dismissal across devices. - */ -public class GlobalDismissManager extends BroadcastReceiver { - public static class AlarmId { - public long mEventId; - public long mStart; - - public AlarmId(long id, long start) { - mEventId = id; - mStart = start; - } - } - - /** - * Globally dismiss notifications that are backed by the same events. - * - * @param context application context - * @param alarmIds Unique identifiers for events that have been dismissed by the user. - * @return true if notification_sender_id is available - */ - public static void dismissGlobally(Context context, List alarmIds) { - Set eventIds = new HashSet(alarmIds.size()); - for (AlarmId alarmId: alarmIds) { - eventIds.add(alarmId.mEventId); - } - } - - @Override - @SuppressWarnings("unchecked") - public void onReceive(Context context, Intent intent) { - new AsyncTask, Void, Void>() { - @Override - protected Void doInBackground(Pair... params) { - return null; - } - }.execute(new Pair(context, intent)); - } -} diff --git a/src/com/android/calendar/alerts/InitAlarmsService.java b/src/com/android/calendar/alerts/InitAlarmsService.java deleted file mode 100644 index 3a9b0b2c..00000000 --- a/src/com/android/calendar/alerts/InitAlarmsService.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2012 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.alerts; - -import android.app.IntentService; -import android.content.ContentValues; -import android.content.Intent; -import android.net.Uri; -import android.os.SystemClock; -import android.provider.CalendarContract; -import android.util.Log; - -/** - * Service for clearing all scheduled alerts from the CalendarAlerts table and - * rescheduling them. This is expected to be called only on boot up, to restore - * the AlarmManager alarms that were lost on device restart. - */ -public class InitAlarmsService extends IntentService { - private static final String TAG = "InitAlarmsService"; - private static final String SCHEDULE_ALARM_REMOVE_PATH = "schedule_alarms_remove"; - private static final Uri SCHEDULE_ALARM_REMOVE_URI = Uri.withAppendedPath( - CalendarContract.CONTENT_URI, SCHEDULE_ALARM_REMOVE_PATH); - - // Delay for rescheduling the alarms must be great enough to minimize race - // conditions with the provider's boot up actions. - private static final long DELAY_MS = 30000; - - public InitAlarmsService() { - super("InitAlarmsService"); - } - - @Override - protected void onHandleIntent(Intent intent) { - // Delay to avoid race condition of in-progress alarm scheduling in provider. - SystemClock.sleep(DELAY_MS); - Log.d(TAG, "Clearing and rescheduling alarms."); - try { - getContentResolver().update(SCHEDULE_ALARM_REMOVE_URI, new ContentValues(), null, - null); - } catch (java.lang.IllegalArgumentException e) { - // java.lang.IllegalArgumentException: - // Unknown URI content://com.android.calendar/schedule_alarms_remove - - // Until b/7742576 is resolved, just catch the exception so the app won't crash - Log.e(TAG, "update failed: " + e.toString()); - } - } -} diff --git a/src/com/android/calendar/alerts/NotificationMgr.java b/src/com/android/calendar/alerts/NotificationMgr.java deleted file mode 100644 index 0ab475c3..00000000 --- a/src/com/android/calendar/alerts/NotificationMgr.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2012 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.alerts; - -import com.android.calendar.alerts.AlertService.NotificationWrapper; - -public abstract class NotificationMgr { - public abstract void notify(int id, NotificationWrapper notification); - public abstract void cancel(int id); - - /** - * Don't actually use the notification framework's cancelAll since the SyncAdapter - * might post notifications and we don't want to affect those. - */ - public void cancelAll() { - cancelAllBetween(0, AlertService.MAX_NOTIFICATIONS); - } - - /** - * Cancels IDs between the specified bounds, inclusively. - */ - public void cancelAllBetween(int from, int to) { - for (int i = from; i <= to; i++) { - cancel(i); - } - } -} diff --git a/src/com/android/calendar/alerts/QuickResponseActivity.java b/src/com/android/calendar/alerts/QuickResponseActivity.java deleted file mode 100644 index 3d291d02..00000000 --- a/src/com/android/calendar/alerts/QuickResponseActivity.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2012 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.alerts; - -import android.app.ListActivity; -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.Toast; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.Arrays; - -/** - * Activity which displays when the user wants to email guests from notifications. - * - * This presents the user with list if quick responses to be populated in an email - * to minimize typing. - * - */ -public class QuickResponseActivity extends ListActivity implements OnItemClickListener { - private static final String TAG = "QuickResponseActivity"; - public static final String EXTRA_EVENT_ID = "eventId"; - - private String[] mResponses = null; - static long mEventId; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - Intent intent = getIntent(); - if (intent == null) { - finish(); - return; - } - - mEventId = intent.getLongExtra(EXTRA_EVENT_ID, -1); - if (mEventId == -1) { - finish(); - return; - } - - // Set listener - getListView().setOnItemClickListener(QuickResponseActivity.this); - - // Populate responses - String[] responses = Utils.getQuickResponses(this); - Arrays.sort(responses); - - // Add "Custom response..." - mResponses = new String[responses.length + 1]; - int i; - for (i = 0; i < responses.length; i++) { - mResponses[i] = responses[i]; - } - mResponses[i] = getResources().getString(R.string.quick_response_custom_msg); - - setListAdapter(new ArrayAdapter(this, R.layout.quick_response_item, mResponses)); - } - - // implements OnItemClickListener - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - - String body = null; - if (mResponses != null && position < mResponses.length - 1) { - body = mResponses[position]; - } - - // Start thread to query provider and send mail - new QueryThread(mEventId, body).start(); - } - - private class QueryThread extends Thread { - long mEventId; - String mBody; - - QueryThread(long eventId, String body) { - mEventId = eventId; - mBody = body; - } - - @Override - public void run() { - } - } -} diff --git a/src/com/android/calendar/month/MonthByWeekAdapter.java b/src/com/android/calendar/month/MonthByWeekAdapter.java deleted file mode 100644 index 45a1bea1..00000000 --- a/src/com/android/calendar/month/MonthByWeekAdapter.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (C) 2010 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.month; - -import android.content.Context; -import android.content.res.Configuration; -import android.os.Handler; -import android.os.Message; -import android.text.format.Time; -import android.util.Log; -import android.view.GestureDetector; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.widget.AbsListView.LayoutParams; - -import com.android.calendar.CalendarController; -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.CalendarController.ViewType; -import com.android.calendar.Event; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.HashMap; - -public class MonthByWeekAdapter extends SimpleWeeksAdapter { - private static final String TAG = "MonthByWeekAdapter"; - - public static final String WEEK_PARAMS_IS_MINI = "mini_month"; - protected static int DEFAULT_QUERY_DAYS = 7 * 8; // 8 weeks - private static final long ANIMATE_TODAY_TIMEOUT = 1000; - - protected CalendarController mController; - protected String mHomeTimeZone; - protected Time mTempTime; - protected Time mToday; - protected int mFirstJulianDay; - protected int mQueryDays; - protected boolean mIsMiniMonth = true; - protected int mOrientation = Configuration.ORIENTATION_LANDSCAPE; - private final boolean mShowAgendaWithMonth; - - protected ArrayList> mEventDayList = new ArrayList>(); - protected ArrayList mEvents = null; - - private boolean mAnimateToday = false; - private long mAnimateTime = 0; - - private Handler mEventDialogHandler; - - MonthWeekEventsView mClickedView; - MonthWeekEventsView mSingleTapUpView; - MonthWeekEventsView mLongClickedView; - - float mClickedXLocation; // Used to find which day was clicked - long mClickTime; // Used to calculate minimum click animation time - // Used to insure minimal time for seeing the click animation before switching views - private static final int mOnTapDelay = 100; - // Minimal time for a down touch action before stating the click animation, this insures that - // there is no click animation on flings - private static int mOnDownDelay; - private static int mTotalClickDelay; - // Minimal distance to move the finger in order to cancel the click animation - private static float mMovedPixelToCancel; - - public MonthByWeekAdapter(Context context, HashMap params) { - super(context, params); - if (params.containsKey(WEEK_PARAMS_IS_MINI)) { - mIsMiniMonth = params.get(WEEK_PARAMS_IS_MINI) != 0; - } - mShowAgendaWithMonth = Utils.getConfigBool(context, R.bool.show_agenda_with_month); - ViewConfiguration vc = ViewConfiguration.get(context); - mOnDownDelay = ViewConfiguration.getTapTimeout(); - mMovedPixelToCancel = vc.getScaledTouchSlop(); - mTotalClickDelay = mOnDownDelay + mOnTapDelay; - } - - public void animateToday() { - mAnimateToday = true; - mAnimateTime = System.currentTimeMillis(); - } - - @Override - protected void init() { - super.init(); - mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener()); - mController = CalendarController.getInstance(mContext); - mHomeTimeZone = Utils.getTimeZone(mContext, null); - mSelectedDay.switchTimezone(mHomeTimeZone); - mToday = new Time(mHomeTimeZone); - mToday.setToNow(); - mTempTime = new Time(mHomeTimeZone); - } - - private void updateTimeZones() { - mSelectedDay.timezone = mHomeTimeZone; - mSelectedDay.normalize(true); - mToday.timezone = mHomeTimeZone; - mToday.setToNow(); - mTempTime.switchTimezone(mHomeTimeZone); - } - - @Override - public void setSelectedDay(Time selectedTime) { - mSelectedDay.set(selectedTime); - long millis = mSelectedDay.normalize(true); - mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek); - notifyDataSetChanged(); - } - - public void setEvents(int firstJulianDay, int numDays, ArrayList events) { - if (mIsMiniMonth) { - if (Log.isLoggable(TAG, Log.ERROR)) { - Log.e(TAG, "Attempted to set events for mini view. Events only supported in full" - + " view."); - } - return; - } - mEvents = events; - mFirstJulianDay = firstJulianDay; - mQueryDays = numDays; - // Create a new list, this is necessary since the weeks are referencing - // pieces of the old list - ArrayList> eventDayList = new ArrayList>(); - for (int i = 0; i < numDays; i++) { - eventDayList.add(new ArrayList()); - } - - if (events == null || events.size() == 0) { - if(Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "No events. Returning early--go schedule something fun."); - } - mEventDayList = eventDayList; - refresh(); - return; - } - - // Compute the new set of days with events - for (Event event : events) { - int startDay = event.startDay - mFirstJulianDay; - int endDay = event.endDay - mFirstJulianDay + 1; - if (startDay < numDays || endDay >= 0) { - if (startDay < 0) { - startDay = 0; - } - if (startDay > numDays) { - continue; - } - if (endDay < 0) { - continue; - } - if (endDay > numDays) { - endDay = numDays; - } - for (int j = startDay; j < endDay; j++) { - eventDayList.get(j).add(event); - } - } - } - if(Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Processed " + events.size() + " events."); - } - mEventDayList = eventDayList; - refresh(); - } - - @SuppressWarnings("unchecked") - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (mIsMiniMonth) { - return super.getView(position, convertView, parent); - } - MonthWeekEventsView v; - LayoutParams params = new LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - HashMap drawingParams = null; - boolean isAnimatingToday = false; - if (convertView != null) { - v = (MonthWeekEventsView) convertView; - // Checking updateToday uses the current params instead of the new - // params, so this is assuming the view is relatively stable - if (mAnimateToday && v.updateToday(mSelectedDay.timezone)) { - long currentTime = System.currentTimeMillis(); - // If it's been too long since we tried to start the animation - // don't show it. This can happen if the user stops a scroll - // before reaching today. - if (currentTime - mAnimateTime > ANIMATE_TODAY_TIMEOUT) { - mAnimateToday = false; - mAnimateTime = 0; - } else { - isAnimatingToday = true; - // There is a bug that causes invalidates to not work some - // of the time unless we recreate the view. - v = new MonthWeekEventsView(mContext); - } - } else { - drawingParams = (HashMap) v.getTag(); - } - } else { - v = new MonthWeekEventsView(mContext); - } - if (drawingParams == null) { - drawingParams = new HashMap(); - } - drawingParams.clear(); - - v.setLayoutParams(params); - v.setClickable(true); - v.setOnTouchListener(this); - - int selectedDay = -1; - if (mSelectedWeek == position) { - selectedDay = mSelectedDay.weekDay; - } - - drawingParams.put(SimpleWeekView.VIEW_PARAMS_HEIGHT, - (parent.getHeight() + parent.getTop()) / mNumWeeks); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, mShowWeekNumber ? 1 : 0); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth); - drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ORIENTATION, mOrientation); - - if (isAnimatingToday) { - drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ANIMATE_TODAY, 1); - mAnimateToday = false; - } - - v.setWeekParams(drawingParams, mSelectedDay.timezone); - return v; - } - - @Override - protected void refresh() { - mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - mShowWeekNumber = Utils.getShowWeekNumber(mContext); - mHomeTimeZone = Utils.getTimeZone(mContext, null); - mOrientation = mContext.getResources().getConfiguration().orientation; - updateTimeZones(); - notifyDataSetChanged(); - } - - @Override - protected void onDayTapped(Time day) { - setDayParameters(day); - if (mShowAgendaWithMonth || mIsMiniMonth) { - // If agenda view is visible with month view , refresh the views - // with the selected day's info - mController.sendEvent(mContext, EventType.GO_TO, day, day, -1, - ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null); - } else { - // Else , switch to the detailed view - mController.sendEvent(mContext, EventType.GO_TO, day, day, -1, - ViewType.DETAIL, - CalendarController.EXTRA_GOTO_DATE - | CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS, null, null); - } - } - - private void setDayParameters(Time day) { - day.timezone = mHomeTimeZone; - Time currTime = new Time(mHomeTimeZone); - currTime.set(mController.getTime()); - day.hour = currTime.hour; - day.minute = currTime.minute; - day.allDay = false; - day.normalize(true); - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - if (!(v instanceof MonthWeekEventsView)) { - return super.onTouch(v, event); - } - - int action = event.getAction(); - - // Event was tapped - switch to the detailed view making sure the click animation - // is done first. - if (mGestureDetector.onTouchEvent(event)) { - mSingleTapUpView = (MonthWeekEventsView) v; - long delay = System.currentTimeMillis() - mClickTime; - // Make sure the animation is visible for at least mOnTapDelay - mOnDownDelay ms - mListView.postDelayed(mDoSingleTapUp, - delay > mTotalClickDelay ? 0 : mTotalClickDelay - delay); - return true; - } else { - // Animate a click - on down: show the selected day in the "clicked" color. - // On Up/scroll/move/cancel: hide the "clicked" color. - switch (action) { - case MotionEvent.ACTION_DOWN: - mClickedView = (MonthWeekEventsView)v; - mClickedXLocation = event.getX(); - mClickTime = System.currentTimeMillis(); - mListView.postDelayed(mDoClick, mOnDownDelay); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_SCROLL: - case MotionEvent.ACTION_CANCEL: - clearClickedView((MonthWeekEventsView)v); - break; - case MotionEvent.ACTION_MOVE: - // No need to cancel on vertical movement, ACTION_SCROLL will do that. - if (Math.abs(event.getX() - mClickedXLocation) > mMovedPixelToCancel) { - clearClickedView((MonthWeekEventsView)v); - } - break; - default: - break; - } - } - // Do not tell the frameworks we consumed the touch action so that fling actions can be - // processed by the fragment. - return false; - } - - /** - * This is here so we can identify events and process them - */ - protected class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { - @Override - public boolean onSingleTapUp(MotionEvent e) { - return true; - } - - @Override - public void onLongPress(MotionEvent e) { - if (mLongClickedView != null) { - Time day = mLongClickedView.getDayFromLocation(mClickedXLocation); - if (day != null) { - mLongClickedView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - Message message = new Message(); - message.obj = day; - } - mLongClickedView.clearClickedDay(); - mLongClickedView = null; - } - } - } - - // Clear the visual cues of the click animation and related running code. - private void clearClickedView(MonthWeekEventsView v) { - mListView.removeCallbacks(mDoClick); - synchronized(v) { - v.clearClickedDay(); - } - mClickedView = null; - } - - // Perform the tap animation in a runnable to allow a delay before showing the tap color. - // This is done to prevent a click animation when a fling is done. - private final Runnable mDoClick = new Runnable() { - @Override - public void run() { - if (mClickedView != null) { - synchronized(mClickedView) { - mClickedView.setClickedDay(mClickedXLocation); - } - mLongClickedView = mClickedView; - mClickedView = null; - // This is a workaround , sometimes the top item on the listview doesn't refresh on - // invalidate, so this forces a re-draw. - mListView.invalidate(); - } - } - }; - - // Performs the single tap operation: go to the tapped day. - // This is done in a runnable to allow the click animation to finish before switching views - private final Runnable mDoSingleTapUp = new Runnable() { - @Override - public void run() { - if (mSingleTapUpView != null) { - Time day = mSingleTapUpView.getDayFromLocation(mClickedXLocation); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Touched day at Row=" + mSingleTapUpView.mWeek + " day=" + day.toString()); - } - if (day != null) { - onDayTapped(day); - } - clearClickedView(mSingleTapUpView); - mSingleTapUpView = null; - } - } - }; -} diff --git a/src/com/android/calendar/month/MonthByWeekFragment.java b/src/com/android/calendar/month/MonthByWeekFragment.java deleted file mode 100644 index f8a518d3..00000000 --- a/src/com/android/calendar/month/MonthByWeekFragment.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright (C) 2010 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.month; - -import android.app.Activity; -import android.app.FragmentManager; -import android.app.LoaderManager; -import android.content.ContentUris; -import android.content.CursorLoader; -import android.content.Loader; -import android.content.res.Resources; -import android.database.Cursor; -import android.graphics.drawable.StateListDrawable; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Instances; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; - -import com.android.calendar.CalendarController; -import com.android.calendar.CalendarController.EventInfo; -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.CalendarController.ViewType; -import com.android.calendar.Event; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.HashMap; -import java.util.List; - -public class MonthByWeekFragment extends SimpleDayPickerFragment implements - CalendarController.EventHandler, LoaderManager.LoaderCallbacks, OnScrollListener, - OnTouchListener { - private static final String TAG = "MonthFragment"; - private static final String TAG_EVENT_DIALOG = "event_dialog"; - - // Selection and selection args for adding event queries - private static final String WHERE_CALENDARS_VISIBLE = Calendars.VISIBLE + "=1"; - private static final String INSTANCES_SORT_ORDER = Instances.START_DAY + "," - + Instances.START_MINUTE + "," + Instances.TITLE; - protected static boolean mShowDetailsInMonth = false; - - protected float mMinimumTwoMonthFlingVelocity; - protected boolean mIsMiniMonth; - protected boolean mHideDeclined; - - protected int mFirstLoadedJulianDay; - protected int mLastLoadedJulianDay; - - private static final int WEEKS_BUFFER = 1; - // How long to wait after scroll stops before starting the loader - // Using scroll duration because scroll state changes don't update - // correctly when a scroll is triggered programmatically. - private static final int LOADER_DELAY = 200; - // The minimum time between requeries of the data if the db is - // changing - private static final int LOADER_THROTTLE_DELAY = 500; - - private CursorLoader mLoader; - private Uri mEventUri; - private final Time mDesiredDay = new Time(); - - private volatile boolean mShouldLoad = true; - private boolean mUserScrolled = false; - - private int mEventsLoadingDelay; - private boolean mShowCalendarControls; - private boolean mIsDetached; - - private final Runnable mTZUpdater = new Runnable() { - @Override - public void run() { - String tz = Utils.getTimeZone(mContext, mTZUpdater); - mSelectedDay.timezone = tz; - mSelectedDay.normalize(true); - mTempTime.timezone = tz; - mFirstDayOfMonth.timezone = tz; - mFirstDayOfMonth.normalize(true); - mFirstVisibleDay.timezone = tz; - mFirstVisibleDay.normalize(true); - if (mAdapter != null) { - mAdapter.refresh(); - } - } - }; - - - private final Runnable mUpdateLoader = new Runnable() { - @Override - public void run() { - synchronized (this) { - if (!mShouldLoad || mLoader == null) { - return; - } - // Stop any previous loads while we update the uri - stopLoader(); - - // Start the loader again - mEventUri = updateUri(); - - mLoader.setUri(mEventUri); - mLoader.startLoading(); - mLoader.onContentChanged(); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Started loader with uri: " + mEventUri); - } - } - } - }; - // Used to load the events when a delay is needed - Runnable mLoadingRunnable = new Runnable() { - @Override - public void run() { - if (!mIsDetached) { - mLoader = (CursorLoader) getLoaderManager().initLoader(0, null, - MonthByWeekFragment.this); - } - } - }; - - - /** - * Updates the uri used by the loader according to the current position of - * the listview. - * - * @return The new Uri to use - */ - private Uri updateUri() { - SimpleWeekView child = (SimpleWeekView) mListView.getChildAt(0); - if (child != null) { - int julianDay = child.getFirstJulianDay(); - mFirstLoadedJulianDay = julianDay; - } - // -1 to ensure we get all day events from any time zone - mTempTime.setJulianDay(mFirstLoadedJulianDay - 1); - long start = mTempTime.toMillis(true); - mLastLoadedJulianDay = mFirstLoadedJulianDay + (mNumWeeks + 2 * WEEKS_BUFFER) * 7; - // +1 to ensure we get all day events from any time zone - mTempTime.setJulianDay(mLastLoadedJulianDay + 1); - long end = mTempTime.toMillis(true); - - // Create a new uri with the updated times - Uri.Builder builder = Instances.CONTENT_URI.buildUpon(); - ContentUris.appendId(builder, start); - ContentUris.appendId(builder, end); - return builder.build(); - } - - // Extract range of julian days from URI - private void updateLoadedDays() { - List pathSegments = mEventUri.getPathSegments(); - int size = pathSegments.size(); - if (size <= 2) { - return; - } - long first = Long.parseLong(pathSegments.get(size - 2)); - long last = Long.parseLong(pathSegments.get(size - 1)); - mTempTime.set(first); - mFirstLoadedJulianDay = Time.getJulianDay(first, mTempTime.gmtoff); - mTempTime.set(last); - mLastLoadedJulianDay = Time.getJulianDay(last, mTempTime.gmtoff); - } - - protected String updateWhere() { - // TODO fix selection/selection args after b/3206641 is fixed - String where = WHERE_CALENDARS_VISIBLE; - if (mHideDeclined || !mShowDetailsInMonth) { - where += " AND " + Instances.SELF_ATTENDEE_STATUS + "!=" - + Attendees.ATTENDEE_STATUS_DECLINED; - } - return where; - } - - private void stopLoader() { - synchronized (mUpdateLoader) { - mHandler.removeCallbacks(mUpdateLoader); - if (mLoader != null) { - mLoader.stopLoading(); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Stopped loader from loading"); - } - } - } - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mTZUpdater.run(); - if (mAdapter != null) { - mAdapter.setSelectedDay(mSelectedDay); - } - mIsDetached = false; - - ViewConfiguration viewConfig = ViewConfiguration.get(activity); - mMinimumTwoMonthFlingVelocity = viewConfig.getScaledMaximumFlingVelocity() / 2; - Resources res = activity.getResources(); - mShowCalendarControls = Utils.getConfigBool(activity, R.bool.show_calendar_controls); - // Synchronized the loading time of the month's events with the animation of the - // calendar controls. - if (mShowCalendarControls) { - mEventsLoadingDelay = res.getInteger(R.integer.calendar_controls_animation_time); - } - mShowDetailsInMonth = res.getBoolean(R.bool.show_details_in_month); - } - - @Override - public void onDetach() { - mIsDetached = true; - super.onDetach(); - if (mShowCalendarControls) { - if (mListView != null) { - mListView.removeCallbacks(mLoadingRunnable); - } - } - } - - @Override - protected void setUpAdapter() { - mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - mShowWeekNumber = Utils.getShowWeekNumber(mContext); - - HashMap weekParams = new HashMap(); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, mShowWeekNumber ? 1 : 0); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek); - weekParams.put(MonthByWeekAdapter.WEEK_PARAMS_IS_MINI, mIsMiniMonth ? 1 : 0); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, - Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff)); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_DAYS_PER_WEEK, mDaysPerWeek); - if (mAdapter == null) { - mAdapter = new MonthByWeekAdapter(getActivity(), weekParams); - mAdapter.registerDataSetObserver(mObserver); - } else { - mAdapter.updateParams(weekParams); - } - mAdapter.notifyDataSetChanged(); - } - - @Override - public View onCreateView( - LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v; - if (mIsMiniMonth) { - v = inflater.inflate(R.layout.month_by_week, container, false); - } else { - v = inflater.inflate(R.layout.full_month_by_week, container, false); - } - mDayNamesHeader = (ViewGroup) v.findViewById(R.id.day_names); - return v; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - mListView.setSelector(new StateListDrawable()); - mListView.setOnTouchListener(this); - - if (!mIsMiniMonth) { - mListView.setBackgroundColor(getResources().getColor(R.color.month_bgcolor)); - } - - // To get a smoother transition when showing this fragment, delay loading of events until - // the fragment is expended fully and the calendar controls are gone. - if (mShowCalendarControls) { - mListView.postDelayed(mLoadingRunnable, mEventsLoadingDelay); - } else { - mLoader = (CursorLoader) getLoaderManager().initLoader(0, null, this); - } - mAdapter.setListView(mListView); - } - - public MonthByWeekFragment() { - this(System.currentTimeMillis(), true); - } - - public MonthByWeekFragment(long initialTime, boolean isMiniMonth) { - super(initialTime); - mIsMiniMonth = isMiniMonth; - } - - @Override - protected void setUpHeader() { - if (mIsMiniMonth) { - super.setUpHeader(); - return; - } - - mDayLabels = new String[7]; - for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { - mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString(i, - DateUtils.LENGTH_MEDIUM).toUpperCase(); - } - } - - // TODO - @Override - public Loader onCreateLoader(int id, Bundle args) { - if (mIsMiniMonth) { - return null; - } - CursorLoader loader; - synchronized (mUpdateLoader) { - mFirstLoadedJulianDay = - Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) - - (mNumWeeks * 7 / 2); - mEventUri = updateUri(); - String where = updateWhere(); - - loader = new CursorLoader( - getActivity(), mEventUri, Event.EVENT_PROJECTION, where, - null /* WHERE_CALENDARS_SELECTED_ARGS */, INSTANCES_SORT_ORDER); - loader.setUpdateThrottle(LOADER_THROTTLE_DELAY); - } - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Returning new loader with uri: " + mEventUri); - } - return loader; - } - - @Override - public void doResumeUpdates() { - mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - mShowWeekNumber = Utils.getShowWeekNumber(mContext); - boolean prevHideDeclined = mHideDeclined; - mHideDeclined = Utils.getHideDeclinedEvents(mContext); - if (prevHideDeclined != mHideDeclined && mLoader != null) { - mLoader.setSelection(updateWhere()); - } - mDaysPerWeek = Utils.getDaysPerWeek(mContext); - updateHeader(); - mAdapter.setSelectedDay(mSelectedDay); - mTZUpdater.run(); - mTodayUpdater.run(); - goTo(mSelectedDay.toMillis(true), false, true, false); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - synchronized (mUpdateLoader) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Found " + data.getCount() + " cursor entries for uri " + mEventUri); - } - CursorLoader cLoader = (CursorLoader) loader; - if (mEventUri == null) { - mEventUri = cLoader.getUri(); - updateLoadedDays(); - } - if (cLoader.getUri().compareTo(mEventUri) != 0) { - // We've started a new query since this loader ran so ignore the - // result - return; - } - ArrayList events = new ArrayList(); - Event.buildEventsFromCursor( - events, data, mContext, mFirstLoadedJulianDay, mLastLoadedJulianDay); - ((MonthByWeekAdapter) mAdapter).setEvents(mFirstLoadedJulianDay, - mLastLoadedJulianDay - mFirstLoadedJulianDay + 1, events); - } - } - - @Override - public void onLoaderReset(Loader loader) { - } - - @Override - public void eventsChanged() { - // TODO remove this after b/3387924 is resolved - if (mLoader != null) { - mLoader.forceLoad(); - } - } - - @Override - public long getSupportedEventTypes() { - return EventType.GO_TO | EventType.EVENTS_CHANGED; - } - - @Override - public void handleEvent(EventInfo event) { - if (event.eventType == EventType.GO_TO) { - boolean animate = true; - if (mDaysPerWeek * mNumWeeks * 2 < Math.abs( - Time.getJulianDay(event.selectedTime.toMillis(true), event.selectedTime.gmtoff) - - Time.getJulianDay(mFirstVisibleDay.toMillis(true), mFirstVisibleDay.gmtoff) - - mDaysPerWeek * mNumWeeks / 2)) { - animate = false; - } - mDesiredDay.set(event.selectedTime); - mDesiredDay.normalize(true); - boolean animateToday = (event.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0; - boolean delayAnimation = goTo(event.selectedTime.toMillis(true), animate, true, false); - if (animateToday) { - // If we need to flash today start the animation after any - // movement from listView has ended. - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - ((MonthByWeekAdapter) mAdapter).animateToday(); - mAdapter.notifyDataSetChanged(); - } - }, delayAnimation ? GOTO_SCROLL_DURATION : 0); - } - } else if (event.eventType == EventType.EVENTS_CHANGED) { - eventsChanged(); - } - } - - @Override - protected void setMonthDisplayed(Time time, boolean updateHighlight) { - super.setMonthDisplayed(time, updateHighlight); - if (!mIsMiniMonth) { - boolean useSelected = false; - if (time.year == mDesiredDay.year && time.month == mDesiredDay.month) { - mSelectedDay.set(mDesiredDay); - mAdapter.setSelectedDay(mDesiredDay); - useSelected = true; - } else { - mSelectedDay.set(time); - mAdapter.setSelectedDay(time); - } - CalendarController controller = CalendarController.getInstance(mContext); - if (mSelectedDay.minute >= 30) { - mSelectedDay.minute = 30; - } else { - mSelectedDay.minute = 0; - } - long newTime = mSelectedDay.normalize(true); - if (newTime != controller.getTime() && mUserScrolled) { - long offset = useSelected ? 0 : DateUtils.WEEK_IN_MILLIS * mNumWeeks / 3; - controller.setTime(newTime + offset); - } - controller.sendEvent(this, EventType.UPDATE_TITLE, time, time, time, -1, - ViewType.CURRENT, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY - | DateUtils.FORMAT_SHOW_YEAR, null, null); - } - } - - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - - synchronized (mUpdateLoader) { - if (scrollState != OnScrollListener.SCROLL_STATE_IDLE) { - mShouldLoad = false; - stopLoader(); - mDesiredDay.setToNow(); - } else { - mHandler.removeCallbacks(mUpdateLoader); - mShouldLoad = true; - mHandler.postDelayed(mUpdateLoader, LOADER_DELAY); - } - } - if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { - mUserScrolled = true; - } - - mScrollStateChangedRunnable.doScrollStateChange(view, scrollState); - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - mDesiredDay.setToNow(); - return false; - } -} diff --git a/src/com/android/calendar/month/MonthListView.java b/src/com/android/calendar/month/MonthListView.java deleted file mode 100644 index f2621ccb..00000000 --- a/src/com/android/calendar/month/MonthListView.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2012 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.month; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.ListView; - -import com.android.calendar.Utils; - -public class MonthListView extends ListView { - - private static final String TAG = "MonthListView"; - - public MonthListView(Context context) { - super(context); - } - - public MonthListView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public MonthListView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - return super.onTouchEvent(ev); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return super.onInterceptTouchEvent(ev); - } -} diff --git a/src/com/android/calendar/month/MonthWeekEventsView.java b/src/com/android/calendar/month/MonthWeekEventsView.java deleted file mode 100644 index e1c78c67..00000000 --- a/src/com/android/calendar/month/MonthWeekEventsView.java +++ /dev/null @@ -1,1110 +0,0 @@ -/* - * Copyright (C) 2010 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.month; - -import com.android.calendar.Event; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.app.Service; -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Paint.Style; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.provider.CalendarContract.Attendees; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.MotionEvent; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Formatter; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; - -public class MonthWeekEventsView extends SimpleWeekView { - - private static final String TAG = "MonthView"; - - private static final boolean DEBUG_LAYOUT = false; - - public static final String VIEW_PARAMS_ORIENTATION = "orientation"; - public static final String VIEW_PARAMS_ANIMATE_TODAY = "animate_today"; - - /* NOTE: these are not constants, and may be multiplied by a scale factor */ - private static int TEXT_SIZE_MONTH_NUMBER = 32; - private static int TEXT_SIZE_EVENT = 12; - private static int TEXT_SIZE_EVENT_TITLE = 14; - private static int TEXT_SIZE_MORE_EVENTS = 12; - private static int TEXT_SIZE_MONTH_NAME = 14; - private static int TEXT_SIZE_WEEK_NUM = 12; - - private static int DNA_MARGIN = 4; - private static int DNA_ALL_DAY_HEIGHT = 4; - private static int DNA_MIN_SEGMENT_HEIGHT = 4; - private static int DNA_WIDTH = 8; - private static int DNA_ALL_DAY_WIDTH = 32; - private static int DNA_SIDE_PADDING = 6; - private static int CONFLICT_COLOR = Color.BLACK; - private static int EVENT_TEXT_COLOR = Color.WHITE; - - private static int DEFAULT_EDGE_SPACING = 0; - private static int SIDE_PADDING_MONTH_NUMBER = 4; - private static int TOP_PADDING_MONTH_NUMBER = 4; - private static int TOP_PADDING_WEEK_NUMBER = 4; - private static int SIDE_PADDING_WEEK_NUMBER = 20; - private static int DAY_SEPARATOR_OUTER_WIDTH = 0; - private static int DAY_SEPARATOR_INNER_WIDTH = 1; - private static int DAY_SEPARATOR_VERTICAL_LENGTH = 53; - private static int DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT = 64; - private static int MIN_WEEK_WIDTH = 50; - - private static int EVENT_X_OFFSET_LANDSCAPE = 38; - private static int EVENT_Y_OFFSET_LANDSCAPE = 8; - private static int EVENT_Y_OFFSET_PORTRAIT = 7; - private static int EVENT_SQUARE_WIDTH = 10; - private static int EVENT_SQUARE_BORDER = 2; - private static int EVENT_LINE_PADDING = 2; - private static int EVENT_RIGHT_PADDING = 4; - private static int EVENT_BOTTOM_PADDING = 3; - - private static int TODAY_HIGHLIGHT_WIDTH = 2; - - private static int SPACING_WEEK_NUMBER = 24; - private static boolean mInitialized = false; - private static boolean mShowDetailsInMonth; - - protected Time mToday = new Time(); - protected boolean mHasToday = false; - protected int mTodayIndex = -1; - protected int mOrientation = Configuration.ORIENTATION_LANDSCAPE; - protected List> mEvents = null; - protected ArrayList mUnsortedEvents = null; - HashMap mDna = null; - // This is for drawing the outlines around event chips and supports up to 10 - // events being drawn on each day. The code will expand this if necessary. - protected FloatRef mEventOutlines = new FloatRef(10 * 4 * 4 * 7); - - - - protected static StringBuilder mStringBuilder = new StringBuilder(50); - // TODO recreate formatter when locale changes - protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); - - protected Paint mMonthNamePaint; - protected TextPaint mEventPaint; - protected TextPaint mSolidBackgroundEventPaint; - protected TextPaint mFramedEventPaint; - protected TextPaint mDeclinedEventPaint; - protected TextPaint mEventExtrasPaint; - protected TextPaint mEventDeclinedExtrasPaint; - protected Paint mWeekNumPaint; - protected Paint mDNAAllDayPaint; - protected Paint mDNATimePaint; - protected Paint mEventSquarePaint; - - - protected Drawable mTodayDrawable; - - protected int mMonthNumHeight; - protected int mMonthNumAscentHeight; - protected int mEventHeight; - protected int mEventAscentHeight; - protected int mExtrasHeight; - protected int mExtrasAscentHeight; - protected int mExtrasDescent; - protected int mWeekNumAscentHeight; - - protected int mMonthBGColor; - protected int mMonthBGOtherColor; - protected int mMonthBGTodayColor; - protected int mMonthNumColor; - protected int mMonthNumOtherColor; - protected int mMonthNumTodayColor; - protected int mMonthNameColor; - protected int mMonthNameOtherColor; - protected int mMonthEventColor; - protected int mMonthDeclinedEventColor; - protected int mMonthDeclinedExtrasColor; - protected int mMonthEventExtraColor; - protected int mMonthEventOtherColor; - protected int mMonthEventExtraOtherColor; - protected int mMonthWeekNumColor; - protected int mMonthBusyBitsBgColor; - protected int mMonthBusyBitsBusyTimeColor; - protected int mMonthBusyBitsConflictTimeColor; - private int mClickedDayIndex = -1; - private int mClickedDayColor; - private static final int mClickedAlpha = 128; - - protected int mEventChipOutlineColor = 0xFFFFFFFF; - protected int mDaySeparatorInnerColor; - protected int mTodayAnimateColor; - - private boolean mAnimateToday; - private int mAnimateTodayAlpha = 0; - private ObjectAnimator mTodayAnimator = null; - - private final TodayAnimatorListener mAnimatorListener = new TodayAnimatorListener(); - - class TodayAnimatorListener extends AnimatorListenerAdapter { - private volatile Animator mAnimator = null; - private volatile boolean mFadingIn = false; - - @Override - public void onAnimationEnd(Animator animation) { - synchronized (this) { - if (mAnimator != animation) { - animation.removeAllListeners(); - animation.cancel(); - return; - } - if (mFadingIn) { - if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners(); - mTodayAnimator.cancel(); - } - mTodayAnimator = ObjectAnimator.ofInt(MonthWeekEventsView.this, - "animateTodayAlpha", 255, 0); - mAnimator = mTodayAnimator; - mFadingIn = false; - mTodayAnimator.addListener(this); - mTodayAnimator.setDuration(600); - mTodayAnimator.start(); - } else { - mAnimateToday = false; - mAnimateTodayAlpha = 0; - mAnimator.removeAllListeners(); - mAnimator = null; - mTodayAnimator = null; - invalidate(); - } - } - } - - public void setAnimator(Animator animation) { - mAnimator = animation; - } - - public void setFadingIn(boolean fadingIn) { - mFadingIn = fadingIn; - } - - } - - private int[] mDayXs; - - /** - * This provides a reference to a float array which allows for easy size - * checking and reallocation. Used for drawing lines. - */ - private class FloatRef { - float[] array; - - public FloatRef(int size) { - array = new float[size]; - } - - public void ensureSize(int newSize) { - if (newSize >= array.length) { - // Add enough space for 7 more boxes to be drawn - array = Arrays.copyOf(array, newSize + 16 * 7); - } - } - } - - /** - * Shows up as an error if we don't include this. - */ - public MonthWeekEventsView(Context context) { - super(context); - } - - // Sets the list of events for this week. Takes a sorted list of arrays - // divided up by day for generating the large month version and the full - // arraylist sorted by start time to generate the dna version. - public void setEvents(List> sortedEvents, ArrayList unsortedEvents) { - setEvents(sortedEvents); - // The MIN_WEEK_WIDTH is a hack to prevent the view from trying to - // generate dna bits before its width has been fixed. - createDna(unsortedEvents); - } - - /** - * Sets up the dna bits for the view. This will return early if the view - * isn't in a state that will create a valid set of dna yet (such as the - * views width not being set correctly yet). - */ - public void createDna(ArrayList unsortedEvents) { - if (unsortedEvents == null || mWidth <= MIN_WEEK_WIDTH || getContext() == null) { - // Stash the list of events for use when this view is ready, or - // just clear it if a null set has been passed to this view - mUnsortedEvents = unsortedEvents; - mDna = null; - return; - } else { - // clear the cached set of events since we're ready to build it now - mUnsortedEvents = null; - } - // Create the drawing coordinates for dna - if (!mShowDetailsInMonth) { - int numDays = mEvents.size(); - int effectiveWidth = mWidth - mPadding * 2; - if (mShowWeekNum) { - effectiveWidth -= SPACING_WEEK_NUMBER; - } - DNA_ALL_DAY_WIDTH = effectiveWidth / numDays - 2 * DNA_SIDE_PADDING; - mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH); - mDayXs = new int[numDays]; - for (int day = 0; day < numDays; day++) { - mDayXs[day] = computeDayLeftPosition(day) + DNA_WIDTH / 2 + DNA_SIDE_PADDING; - - } - - int top = DAY_SEPARATOR_INNER_WIDTH + DNA_MARGIN + DNA_ALL_DAY_HEIGHT + 1; - int bottom = mHeight - DNA_MARGIN; - mDna = Utils.createDNAStrands(mFirstJulianDay, unsortedEvents, top, bottom, - DNA_MIN_SEGMENT_HEIGHT, mDayXs, getContext()); - } - } - - public void setEvents(List> sortedEvents) { - mEvents = sortedEvents; - if (sortedEvents == null) { - return; - } - if (sortedEvents.size() != mNumDays) { - if (Log.isLoggable(TAG, Log.ERROR)) { - Log.wtf(TAG, "Events size must be same as days displayed: size=" - + sortedEvents.size() + " days=" + mNumDays); - } - mEvents = null; - return; - } - } - - protected void loadColors(Context context) { - Resources res = context.getResources(); - mMonthWeekNumColor = res.getColor(R.color.month_week_num_color); - mMonthNumColor = res.getColor(R.color.month_day_number); - mMonthNumOtherColor = res.getColor(R.color.month_day_number_other); - mMonthNumTodayColor = res.getColor(R.color.month_today_number); - mMonthNameColor = mMonthNumColor; - mMonthNameOtherColor = mMonthNumOtherColor; - mMonthEventColor = res.getColor(R.color.month_event_color); - mMonthDeclinedEventColor = res.getColor(R.color.agenda_item_declined_color); - mMonthDeclinedExtrasColor = res.getColor(R.color.agenda_item_where_declined_text_color); - mMonthEventExtraColor = res.getColor(R.color.month_event_extra_color); - mMonthEventOtherColor = res.getColor(R.color.month_event_other_color); - mMonthEventExtraOtherColor = res.getColor(R.color.month_event_extra_other_color); - mMonthBGTodayColor = res.getColor(R.color.month_today_bgcolor); - mMonthBGOtherColor = res.getColor(R.color.month_other_bgcolor); - mMonthBGColor = res.getColor(R.color.month_bgcolor); - mDaySeparatorInnerColor = res.getColor(R.color.month_grid_lines); - mTodayAnimateColor = res.getColor(R.color.today_highlight_color); - mClickedDayColor = res.getColor(R.color.day_clicked_background_color); - mTodayDrawable = res.getDrawable(R.drawable.today_blue_week_holo_light); - } - - /** - * Sets up the text and style properties for painting. Override this if you - * want to use a different paint. - */ - @Override - protected void initView() { - super.initView(); - - if (!mInitialized) { - Resources resources = getContext().getResources(); - mShowDetailsInMonth = Utils.getConfigBool(getContext(), R.bool.show_details_in_month); - TEXT_SIZE_EVENT_TITLE = resources.getInteger(R.integer.text_size_event_title); - TEXT_SIZE_MONTH_NUMBER = resources.getInteger(R.integer.text_size_month_number); - SIDE_PADDING_MONTH_NUMBER = resources.getInteger(R.integer.month_day_number_margin); - CONFLICT_COLOR = resources.getColor(R.color.month_dna_conflict_time_color); - EVENT_TEXT_COLOR = resources.getColor(R.color.calendar_event_text_color); - if (mScale != 1) { - TOP_PADDING_MONTH_NUMBER *= mScale; - TOP_PADDING_WEEK_NUMBER *= mScale; - SIDE_PADDING_MONTH_NUMBER *= mScale; - SIDE_PADDING_WEEK_NUMBER *= mScale; - SPACING_WEEK_NUMBER *= mScale; - TEXT_SIZE_MONTH_NUMBER *= mScale; - TEXT_SIZE_EVENT *= mScale; - TEXT_SIZE_EVENT_TITLE *= mScale; - TEXT_SIZE_MORE_EVENTS *= mScale; - TEXT_SIZE_MONTH_NAME *= mScale; - TEXT_SIZE_WEEK_NUM *= mScale; - DAY_SEPARATOR_OUTER_WIDTH *= mScale; - DAY_SEPARATOR_INNER_WIDTH *= mScale; - DAY_SEPARATOR_VERTICAL_LENGTH *= mScale; - DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT *= mScale; - EVENT_X_OFFSET_LANDSCAPE *= mScale; - EVENT_Y_OFFSET_LANDSCAPE *= mScale; - EVENT_Y_OFFSET_PORTRAIT *= mScale; - EVENT_SQUARE_WIDTH *= mScale; - EVENT_SQUARE_BORDER *= mScale; - EVENT_LINE_PADDING *= mScale; - EVENT_BOTTOM_PADDING *= mScale; - EVENT_RIGHT_PADDING *= mScale; - DNA_MARGIN *= mScale; - DNA_WIDTH *= mScale; - DNA_ALL_DAY_HEIGHT *= mScale; - DNA_MIN_SEGMENT_HEIGHT *= mScale; - DNA_SIDE_PADDING *= mScale; - DEFAULT_EDGE_SPACING *= mScale; - DNA_ALL_DAY_WIDTH *= mScale; - TODAY_HIGHLIGHT_WIDTH *= mScale; - } - if (!mShowDetailsInMonth) { - TOP_PADDING_MONTH_NUMBER += DNA_ALL_DAY_HEIGHT + DNA_MARGIN; - } - mInitialized = true; - } - mPadding = DEFAULT_EDGE_SPACING; - loadColors(getContext()); - // TODO modify paint properties depending on isMini - - mMonthNumPaint = new Paint(); - mMonthNumPaint.setFakeBoldText(false); - mMonthNumPaint.setAntiAlias(true); - mMonthNumPaint.setTextSize(TEXT_SIZE_MONTH_NUMBER); - mMonthNumPaint.setColor(mMonthNumColor); - mMonthNumPaint.setStyle(Style.FILL); - mMonthNumPaint.setTextAlign(Align.RIGHT); - mMonthNumPaint.setTypeface(Typeface.DEFAULT); - - mMonthNumAscentHeight = (int) (-mMonthNumPaint.ascent() + 0.5f); - mMonthNumHeight = (int) (mMonthNumPaint.descent() - mMonthNumPaint.ascent() + 0.5f); - - mEventPaint = new TextPaint(); - mEventPaint.setFakeBoldText(true); - mEventPaint.setAntiAlias(true); - mEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE); - mEventPaint.setColor(mMonthEventColor); - - mSolidBackgroundEventPaint = new TextPaint(mEventPaint); - mSolidBackgroundEventPaint.setColor(EVENT_TEXT_COLOR); - mFramedEventPaint = new TextPaint(mSolidBackgroundEventPaint); - - mDeclinedEventPaint = new TextPaint(); - mDeclinedEventPaint.setFakeBoldText(true); - mDeclinedEventPaint.setAntiAlias(true); - mDeclinedEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE); - mDeclinedEventPaint.setColor(mMonthDeclinedEventColor); - - mEventAscentHeight = (int) (-mEventPaint.ascent() + 0.5f); - mEventHeight = (int) (mEventPaint.descent() - mEventPaint.ascent() + 0.5f); - - mEventExtrasPaint = new TextPaint(); - mEventExtrasPaint.setFakeBoldText(false); - mEventExtrasPaint.setAntiAlias(true); - mEventExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER); - mEventExtrasPaint.setTextSize(TEXT_SIZE_EVENT); - mEventExtrasPaint.setColor(mMonthEventExtraColor); - mEventExtrasPaint.setStyle(Style.FILL); - mEventExtrasPaint.setTextAlign(Align.LEFT); - mExtrasHeight = (int)(mEventExtrasPaint.descent() - mEventExtrasPaint.ascent() + 0.5f); - mExtrasAscentHeight = (int)(-mEventExtrasPaint.ascent() + 0.5f); - mExtrasDescent = (int)(mEventExtrasPaint.descent() + 0.5f); - - mEventDeclinedExtrasPaint = new TextPaint(); - mEventDeclinedExtrasPaint.setFakeBoldText(false); - mEventDeclinedExtrasPaint.setAntiAlias(true); - mEventDeclinedExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER); - mEventDeclinedExtrasPaint.setTextSize(TEXT_SIZE_EVENT); - mEventDeclinedExtrasPaint.setColor(mMonthDeclinedExtrasColor); - mEventDeclinedExtrasPaint.setStyle(Style.FILL); - mEventDeclinedExtrasPaint.setTextAlign(Align.LEFT); - - mWeekNumPaint = new Paint(); - mWeekNumPaint.setFakeBoldText(false); - mWeekNumPaint.setAntiAlias(true); - mWeekNumPaint.setTextSize(TEXT_SIZE_WEEK_NUM); - mWeekNumPaint.setColor(mWeekNumColor); - mWeekNumPaint.setStyle(Style.FILL); - mWeekNumPaint.setTextAlign(Align.RIGHT); - - mWeekNumAscentHeight = (int) (-mWeekNumPaint.ascent() + 0.5f); - - mDNAAllDayPaint = new Paint(); - mDNATimePaint = new Paint(); - mDNATimePaint.setColor(mMonthBusyBitsBusyTimeColor); - mDNATimePaint.setStyle(Style.FILL_AND_STROKE); - mDNATimePaint.setStrokeWidth(DNA_WIDTH); - mDNATimePaint.setAntiAlias(false); - mDNAAllDayPaint.setColor(mMonthBusyBitsConflictTimeColor); - mDNAAllDayPaint.setStyle(Style.FILL_AND_STROKE); - mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH); - mDNAAllDayPaint.setAntiAlias(false); - - mEventSquarePaint = new Paint(); - mEventSquarePaint.setStrokeWidth(EVENT_SQUARE_BORDER); - mEventSquarePaint.setAntiAlias(false); - - if (DEBUG_LAYOUT) { - Log.d("EXTRA", "mScale=" + mScale); - Log.d("EXTRA", "mMonthNumPaint ascent=" + mMonthNumPaint.ascent() - + " descent=" + mMonthNumPaint.descent() + " int height=" + mMonthNumHeight); - Log.d("EXTRA", "mEventPaint ascent=" + mEventPaint.ascent() - + " descent=" + mEventPaint.descent() + " int height=" + mEventHeight - + " int ascent=" + mEventAscentHeight); - Log.d("EXTRA", "mEventExtrasPaint ascent=" + mEventExtrasPaint.ascent() - + " descent=" + mEventExtrasPaint.descent() + " int height=" + mExtrasHeight); - Log.d("EXTRA", "mWeekNumPaint ascent=" + mWeekNumPaint.ascent() - + " descent=" + mWeekNumPaint.descent()); - } - } - - @Override - public void setWeekParams(HashMap params, String tz) { - super.setWeekParams(params, tz); - - if (params.containsKey(VIEW_PARAMS_ORIENTATION)) { - mOrientation = params.get(VIEW_PARAMS_ORIENTATION); - } - - updateToday(tz); - mNumCells = mNumDays + 1; - - if (params.containsKey(VIEW_PARAMS_ANIMATE_TODAY) && mHasToday) { - synchronized (mAnimatorListener) { - if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners(); - mTodayAnimator.cancel(); - } - mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha", - Math.max(mAnimateTodayAlpha, 80), 255); - mTodayAnimator.setDuration(150); - mAnimatorListener.setAnimator(mTodayAnimator); - mAnimatorListener.setFadingIn(true); - mTodayAnimator.addListener(mAnimatorListener); - mAnimateToday = true; - mTodayAnimator.start(); - } - } - } - - /** - * @param tz - */ - public boolean updateToday(String tz) { - mToday.timezone = tz; - mToday.setToNow(); - mToday.normalize(true); - int julianToday = Time.getJulianDay(mToday.toMillis(false), mToday.gmtoff); - if (julianToday >= mFirstJulianDay && julianToday < mFirstJulianDay + mNumDays) { - mHasToday = true; - mTodayIndex = julianToday - mFirstJulianDay; - } else { - mHasToday = false; - mTodayIndex = -1; - } - return mHasToday; - } - - public void setAnimateTodayAlpha(int alpha) { - mAnimateTodayAlpha = alpha; - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - drawBackground(canvas); - drawWeekNums(canvas); - drawDaySeparators(canvas); - if (mHasToday && mAnimateToday) { - drawToday(canvas); - } - if (mShowDetailsInMonth) { - drawEvents(canvas); - } else { - if (mDna == null && mUnsortedEvents != null) { - createDna(mUnsortedEvents); - } - drawDNA(canvas); - } - drawClick(canvas); - } - - protected void drawToday(Canvas canvas) { - r.top = DAY_SEPARATOR_INNER_WIDTH + (TODAY_HIGHLIGHT_WIDTH / 2); - r.bottom = mHeight - (int) Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f); - p.setStyle(Style.STROKE); - p.setStrokeWidth(TODAY_HIGHLIGHT_WIDTH); - r.left = computeDayLeftPosition(mTodayIndex) + (TODAY_HIGHLIGHT_WIDTH / 2); - r.right = computeDayLeftPosition(mTodayIndex + 1) - - (int) Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f); - p.setColor(mTodayAnimateColor | (mAnimateTodayAlpha << 24)); - canvas.drawRect(r, p); - p.setStyle(Style.FILL); - } - - // TODO move into SimpleWeekView - // Computes the x position for the left side of the given day - private int computeDayLeftPosition(int day) { - int effectiveWidth = mWidth; - int x = 0; - int xOffset = 0; - if (mShowWeekNum) { - xOffset = SPACING_WEEK_NUMBER + mPadding; - effectiveWidth -= xOffset; - } - x = day * effectiveWidth / mNumDays + xOffset; - return x; - } - - @Override - protected void drawDaySeparators(Canvas canvas) { - float lines[] = new float[8 * 4]; - int count = 6 * 4; - int wkNumOffset = 0; - int i = 0; - if (mShowWeekNum) { - // This adds the first line separating the week number - int xOffset = SPACING_WEEK_NUMBER + mPadding; - count += 4; - lines[i++] = xOffset; - lines[i++] = 0; - lines[i++] = xOffset; - lines[i++] = mHeight; - wkNumOffset++; - } - count += 4; - lines[i++] = 0; - lines[i++] = 0; - lines[i++] = mWidth; - lines[i++] = 0; - int y0 = 0; - int y1 = mHeight; - - while (i < count) { - int x = computeDayLeftPosition(i / 4 - wkNumOffset); - lines[i++] = x; - lines[i++] = y0; - lines[i++] = x; - lines[i++] = y1; - } - p.setColor(mDaySeparatorInnerColor); - p.setStrokeWidth(DAY_SEPARATOR_INNER_WIDTH); - canvas.drawLines(lines, 0, count, p); - } - - @Override - protected void drawBackground(Canvas canvas) { - int i = 0; - int offset = 0; - r.top = DAY_SEPARATOR_INNER_WIDTH; - r.bottom = mHeight; - if (mShowWeekNum) { - i++; - offset++; - } - if (!mOddMonth[i]) { - while (++i < mOddMonth.length && !mOddMonth[i]) - ; - r.right = computeDayLeftPosition(i - offset); - r.left = 0; - p.setColor(mMonthBGOtherColor); - canvas.drawRect(r, p); - // compute left edge for i, set up r, draw - } else if (!mOddMonth[(i = mOddMonth.length - 1)]) { - while (--i >= offset && !mOddMonth[i]) - ; - i++; - // compute left edge for i, set up r, draw - r.right = mWidth; - r.left = computeDayLeftPosition(i - offset); - p.setColor(mMonthBGOtherColor); - canvas.drawRect(r, p); - } - if (mHasToday) { - p.setColor(mMonthBGTodayColor); - r.left = computeDayLeftPosition(mTodayIndex); - r.right = computeDayLeftPosition(mTodayIndex + 1); - canvas.drawRect(r, p); - } - } - - // Draw the "clicked" color on the tapped day - private void drawClick(Canvas canvas) { - if (mClickedDayIndex != -1) { - int alpha = p.getAlpha(); - p.setColor(mClickedDayColor); - p.setAlpha(mClickedAlpha); - r.left = computeDayLeftPosition(mClickedDayIndex); - r.right = computeDayLeftPosition(mClickedDayIndex + 1); - r.top = DAY_SEPARATOR_INNER_WIDTH; - r.bottom = mHeight; - canvas.drawRect(r, p); - p.setAlpha(alpha); - } - } - - @Override - protected void drawWeekNums(Canvas canvas) { - int y; - - int i = 0; - int offset = -1; - int todayIndex = mTodayIndex; - int x = 0; - int numCount = mNumDays; - if (mShowWeekNum) { - x = SIDE_PADDING_WEEK_NUMBER + mPadding; - y = mWeekNumAscentHeight + TOP_PADDING_WEEK_NUMBER; - canvas.drawText(mDayNumbers[0], x, y, mWeekNumPaint); - numCount++; - i++; - todayIndex++; - offset++; - - } - - y = mMonthNumAscentHeight + TOP_PADDING_MONTH_NUMBER; - - boolean isFocusMonth = mFocusDay[i]; - boolean isBold = false; - mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); - for (; i < numCount; i++) { - if (mHasToday && todayIndex == i) { - mMonthNumPaint.setColor(mMonthNumTodayColor); - mMonthNumPaint.setFakeBoldText(isBold = true); - if (i + 1 < numCount) { - // Make sure the color will be set back on the next - // iteration - isFocusMonth = !mFocusDay[i + 1]; - } - } else if (mFocusDay[i] != isFocusMonth) { - isFocusMonth = mFocusDay[i]; - mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); - } - x = computeDayLeftPosition(i - offset) - (SIDE_PADDING_MONTH_NUMBER); - canvas.drawText(mDayNumbers[i], x, y, mMonthNumPaint); - if (isBold) { - mMonthNumPaint.setFakeBoldText(isBold = false); - } - } - } - - protected void drawEvents(Canvas canvas) { - if (mEvents == null) { - return; - } - - int day = -1; - for (ArrayList eventDay : mEvents) { - day++; - if (eventDay == null || eventDay.size() == 0) { - continue; - } - int ySquare; - int xSquare = computeDayLeftPosition(day) + SIDE_PADDING_MONTH_NUMBER + 1; - int rightEdge = computeDayLeftPosition(day + 1); - - if (mOrientation == Configuration.ORIENTATION_PORTRAIT) { - ySquare = EVENT_Y_OFFSET_PORTRAIT + mMonthNumHeight + TOP_PADDING_MONTH_NUMBER; - rightEdge -= SIDE_PADDING_MONTH_NUMBER + 1; - } else { - ySquare = EVENT_Y_OFFSET_LANDSCAPE; - rightEdge -= EVENT_X_OFFSET_LANDSCAPE; - } - - // Determine if everything will fit when time ranges are shown. - boolean showTimes = true; - Iterator iter = eventDay.iterator(); - int yTest = ySquare; - while (iter.hasNext()) { - Event event = iter.next(); - int newY = drawEvent(canvas, event, xSquare, yTest, rightEdge, iter.hasNext(), - showTimes, /*doDraw*/ false); - if (newY == yTest) { - showTimes = false; - break; - } - yTest = newY; - } - - int eventCount = 0; - iter = eventDay.iterator(); - while (iter.hasNext()) { - Event event = iter.next(); - int newY = drawEvent(canvas, event, xSquare, ySquare, rightEdge, iter.hasNext(), - showTimes, /*doDraw*/ true); - if (newY == ySquare) { - break; - } - eventCount++; - ySquare = newY; - } - - int remaining = eventDay.size() - eventCount; - if (remaining > 0) { - drawMoreEvents(canvas, remaining, xSquare); - } - } - } - - protected int addChipOutline(FloatRef lines, int count, int x, int y) { - lines.ensureSize(count + 16); - // top of box - lines.array[count++] = x; - lines.array[count++] = y; - lines.array[count++] = x + EVENT_SQUARE_WIDTH; - lines.array[count++] = y; - // right side of box - lines.array[count++] = x + EVENT_SQUARE_WIDTH; - lines.array[count++] = y; - lines.array[count++] = x + EVENT_SQUARE_WIDTH; - lines.array[count++] = y + EVENT_SQUARE_WIDTH; - // left side of box - lines.array[count++] = x; - lines.array[count++] = y; - lines.array[count++] = x; - lines.array[count++] = y + EVENT_SQUARE_WIDTH + 1; - // bottom of box - lines.array[count++] = x; - lines.array[count++] = y + EVENT_SQUARE_WIDTH; - lines.array[count++] = x + EVENT_SQUARE_WIDTH + 1; - lines.array[count++] = y + EVENT_SQUARE_WIDTH; - - return count; - } - - /** - * Attempts to draw the given event. Returns the y for the next event or the - * original y if the event will not fit. An event is considered to not fit - * if the event and its extras won't fit or if there are more events and the - * more events line would not fit after drawing this event. - * - * @param canvas the canvas to draw on - * @param event the event to draw - * @param x the top left corner for this event's color chip - * @param y the top left corner for this event's color chip - * @param rightEdge the rightmost point we're allowed to draw on (exclusive) - * @param moreEvents indicates whether additional events will follow this one - * @param showTimes if set, a second line with a time range will be displayed for non-all-day - * events - * @param doDraw if set, do the actual drawing; otherwise this just computes the height - * and returns - * @return the y for the next event or the original y if it won't fit - */ - protected int drawEvent(Canvas canvas, Event event, int x, int y, int rightEdge, - boolean moreEvents, boolean showTimes, boolean doDraw) { - /* - * Vertical layout: - * (top of box) - * a. EVENT_Y_OFFSET_LANDSCAPE or portrait equivalent - * b. Event title: mEventHeight for a normal event, + 2xBORDER_SPACE for all-day event - * c. [optional] Time range (mExtrasHeight) - * d. EVENT_LINE_PADDING - * - * Repeat (b,c,d) as needed and space allows. If we have more events than fit, we need - * to leave room for something like "+2" at the bottom: - * - * e. "+ more" line (mExtrasHeight) - * - * f. EVENT_BOTTOM_PADDING (overlaps EVENT_LINE_PADDING) - * (bottom of box) - */ - final int BORDER_SPACE = EVENT_SQUARE_BORDER + 1; // want a 1-pixel gap inside border - final int STROKE_WIDTH_ADJ = EVENT_SQUARE_BORDER / 2; // adjust bounds for stroke width - boolean allDay = event.allDay; - int eventRequiredSpace = mEventHeight; - if (allDay) { - // Add a few pixels for the box we draw around all-day events. - eventRequiredSpace += BORDER_SPACE * 2; - } else if (showTimes) { - // Need room for the "1pm - 2pm" line. - eventRequiredSpace += mExtrasHeight; - } - int reservedSpace = EVENT_BOTTOM_PADDING; // leave a bit of room at the bottom - if (moreEvents) { - // More events follow. Leave a bit of space between events. - eventRequiredSpace += EVENT_LINE_PADDING; - - // Make sure we have room for the "+ more" line. (The "+ more" line is expected - // to be <= the height of an event line, so we won't show "+1" when we could be - // showing the event.) - reservedSpace += mExtrasHeight; - } - - if (y + eventRequiredSpace + reservedSpace > mHeight) { - // Not enough space, return original y - return y; - } else if (!doDraw) { - return y + eventRequiredSpace; - } - - boolean isDeclined = event.selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED; - int color = event.color; - if (isDeclined) { - color = Utils.getDeclinedColorFromColor(color); - } - - int textX, textY, textRightEdge; - - if (allDay) { - // We shift the render offset "inward", because drawRect with a stroke width greater - // than 1 draws outside the specified bounds. (We don't adjust the left edge, since - // we want to match the existing appearance of the "event square".) - r.left = x; - r.right = rightEdge - STROKE_WIDTH_ADJ; - r.top = y + STROKE_WIDTH_ADJ; - r.bottom = y + mEventHeight + BORDER_SPACE * 2 - STROKE_WIDTH_ADJ; - textX = x + BORDER_SPACE; - textY = y + mEventAscentHeight + BORDER_SPACE; - textRightEdge = rightEdge - BORDER_SPACE; - } else { - r.left = x; - r.right = x + EVENT_SQUARE_WIDTH; - r.bottom = y + mEventAscentHeight; - r.top = r.bottom - EVENT_SQUARE_WIDTH; - textX = x + EVENT_SQUARE_WIDTH + EVENT_RIGHT_PADDING; - textY = y + mEventAscentHeight; - textRightEdge = rightEdge; - } - - Style boxStyle = Style.STROKE; - boolean solidBackground = false; - if (event.selfAttendeeStatus != Attendees.ATTENDEE_STATUS_INVITED) { - boxStyle = Style.FILL_AND_STROKE; - if (allDay) { - solidBackground = true; - } - } - mEventSquarePaint.setStyle(boxStyle); - mEventSquarePaint.setColor(color); - canvas.drawRect(r, mEventSquarePaint); - - float avail = textRightEdge - textX; - CharSequence text = TextUtils.ellipsize( - event.title, mEventPaint, avail, TextUtils.TruncateAt.END); - Paint textPaint; - if (solidBackground) { - // Text color needs to contrast with solid background. - textPaint = mSolidBackgroundEventPaint; - } else if (isDeclined) { - // Use "declined event" color. - textPaint = mDeclinedEventPaint; - } else if (allDay) { - // Text inside frame is same color as frame. - mFramedEventPaint.setColor(color); - textPaint = mFramedEventPaint; - } else { - // Use generic event text color. - textPaint = mEventPaint; - } - canvas.drawText(text.toString(), textX, textY, textPaint); - y += mEventHeight; - if (allDay) { - y += BORDER_SPACE * 2; - } - - if (showTimes && !allDay) { - // show start/end time, e.g. "1pm - 2pm" - textY = y + mExtrasAscentHeight; - mStringBuilder.setLength(0); - text = DateUtils.formatDateRange(getContext(), mFormatter, event.startMillis, - event.endMillis, DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL, - Utils.getTimeZone(getContext(), null)).toString(); - text = TextUtils.ellipsize(text, mEventExtrasPaint, avail, TextUtils.TruncateAt.END); - canvas.drawText(text.toString(), textX, textY, isDeclined ? mEventDeclinedExtrasPaint - : mEventExtrasPaint); - y += mExtrasHeight; - } - - y += EVENT_LINE_PADDING; - - return y; - } - - protected void drawMoreEvents(Canvas canvas, int remainingEvents, int x) { - int y = mHeight - (mExtrasDescent + EVENT_BOTTOM_PADDING); - String text = getContext().getResources().getQuantityString( - R.plurals.month_more_events, remainingEvents); - mEventExtrasPaint.setAntiAlias(true); - mEventExtrasPaint.setFakeBoldText(true); - canvas.drawText(String.format(text, remainingEvents), x, y, mEventExtrasPaint); - mEventExtrasPaint.setFakeBoldText(false); - } - - /** - * Draws a line showing busy times in each day of week The method draws - * non-conflicting times in the event color and times with conflicting - * events in the dna conflict color defined in colors. - * - * @param canvas - */ - protected void drawDNA(Canvas canvas) { - // Draw event and conflict times - if (mDna != null) { - for (Utils.DNAStrand strand : mDna.values()) { - if (strand.color == CONFLICT_COLOR || strand.points == null - || strand.points.length == 0) { - continue; - } - mDNATimePaint.setColor(strand.color); - canvas.drawLines(strand.points, mDNATimePaint); - } - // Draw black last to make sure it's on top - Utils.DNAStrand strand = mDna.get(CONFLICT_COLOR); - if (strand != null && strand.points != null && strand.points.length != 0) { - mDNATimePaint.setColor(strand.color); - canvas.drawLines(strand.points, mDNATimePaint); - } - if (mDayXs == null) { - return; - } - int numDays = mDayXs.length; - int xOffset = (DNA_ALL_DAY_WIDTH - DNA_WIDTH) / 2; - if (strand != null && strand.allDays != null && strand.allDays.length == numDays) { - for (int i = 0; i < numDays; i++) { - // this adds at most 7 draws. We could sort it by color and - // build an array instead but this is easier. - if (strand.allDays[i] != 0) { - mDNAAllDayPaint.setColor(strand.allDays[i]); - canvas.drawLine(mDayXs[i] + xOffset, DNA_MARGIN, mDayXs[i] + xOffset, - DNA_MARGIN + DNA_ALL_DAY_HEIGHT, mDNAAllDayPaint); - } - } - } - } - } - - @Override - protected void updateSelectionPositions() { - if (mHasSelectedDay) { - int selectedPosition = mSelectedDay - mWeekStart; - if (selectedPosition < 0) { - selectedPosition += 7; - } - int effectiveWidth = mWidth - mPadding * 2; - effectiveWidth -= SPACING_WEEK_NUMBER; - mSelectedLeft = selectedPosition * effectiveWidth / mNumDays + mPadding; - mSelectedRight = (selectedPosition + 1) * effectiveWidth / mNumDays + mPadding; - mSelectedLeft += SPACING_WEEK_NUMBER; - mSelectedRight += SPACING_WEEK_NUMBER; - } - } - - public int getDayIndexFromLocation(float x) { - int dayStart = mShowWeekNum ? SPACING_WEEK_NUMBER + mPadding : mPadding; - if (x < dayStart || x > mWidth - mPadding) { - return -1; - } - // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels - return ((int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding))); - } - - @Override - public Time getDayFromLocation(float x) { - int dayPosition = getDayIndexFromLocation(x); - if (dayPosition == -1) { - return null; - } - int day = mFirstJulianDay + dayPosition; - - Time time = new Time(mTimeZone); - if (mWeek == 0) { - // This week is weird... - if (day < Time.EPOCH_JULIAN_DAY) { - day++; - } else if (day == Time.EPOCH_JULIAN_DAY) { - time.set(1, 0, 1970); - time.normalize(true); - return time; - } - } - - time.setJulianDay(day); - return time; - } - - @Override - public boolean onHoverEvent(MotionEvent event) { - Context context = getContext(); - // only send accessibility events if accessibility and exploration are - // on. - AccessibilityManager am = (AccessibilityManager) context - .getSystemService(Service.ACCESSIBILITY_SERVICE); - if (!am.isEnabled() || !am.isTouchExplorationEnabled()) { - return super.onHoverEvent(event); - } - if (event.getAction() != MotionEvent.ACTION_HOVER_EXIT) { - Time hover = getDayFromLocation(event.getX()); - if (hover != null - && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) != 0)) { - Long millis = hover.toMillis(true); - String date = Utils.formatDateRange(context, millis, millis, - DateUtils.FORMAT_SHOW_DATE); - AccessibilityEvent accessEvent = AccessibilityEvent - .obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); - accessEvent.getText().add(date); - if (mShowDetailsInMonth && mEvents != null) { - int dayStart = SPACING_WEEK_NUMBER + mPadding; - int dayPosition = (int) ((event.getX() - dayStart) * mNumDays / (mWidth - - dayStart - mPadding)); - ArrayList events = mEvents.get(dayPosition); - List text = accessEvent.getText(); - for (Event e : events) { - text.add(e.getTitleAndLocation() + ". "); - int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; - if (!e.allDay) { - flags |= DateUtils.FORMAT_SHOW_TIME; - if (DateFormat.is24HourFormat(context)) { - flags |= DateUtils.FORMAT_24HOUR; - } - } else { - flags |= DateUtils.FORMAT_UTC; - } - text.add(Utils.formatDateRange(context, e.startMillis, e.endMillis, - flags) + ". "); - } - } - sendAccessibilityEventUnchecked(accessEvent); - mLastHoverTime = hover; - } - } - return true; - } - - public void setClickedDay(float xLocation) { - mClickedDayIndex = getDayIndexFromLocation(xLocation); - invalidate(); - } - public void clearClickedDay() { - mClickedDayIndex = -1; - invalidate(); - } -} diff --git a/src/com/android/calendar/month/SimpleDayPickerFragment.java b/src/com/android/calendar/month/SimpleDayPickerFragment.java deleted file mode 100644 index 2efae6a9..00000000 --- a/src/com/android/calendar/month/SimpleDayPickerFragment.java +++ /dev/null @@ -1,612 +0,0 @@ -/* - * Copyright (C) 2010 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.month; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.app.Activity; -import android.app.ListFragment; -import android.content.Context; -import android.content.res.Resources; -import android.database.DataSetObserver; -import android.os.Bundle; -import android.os.Handler; -import android.text.TextUtils; -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.ViewConfiguration; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityEvent; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; -import android.widget.ListView; -import android.widget.TextView; - -import java.util.Calendar; -import java.util.HashMap; -import java.util.Locale; - -/** - *

- * This displays a titled list of weeks with selectable days. It can be - * configured to display the week number, start the week on a given day, show a - * reduced number of days, or display an arbitrary number of weeks at a time. By - * overriding methods and changing variables this fragment can be customized to - * easily display a month selection component in a given style. - *

- */ -public class SimpleDayPickerFragment extends ListFragment implements OnScrollListener { - - private static final String TAG = "MonthFragment"; - private static final String KEY_CURRENT_TIME = "current_time"; - - // Affects when the month selection will change while scrolling up - protected static final int SCROLL_HYST_WEEKS = 2; - // How long the GoTo fling animation should last - protected static final int GOTO_SCROLL_DURATION = 500; - // How long to wait after receiving an onScrollStateChanged notification - // before acting on it - protected static final int SCROLL_CHANGE_DELAY = 40; - // The number of days to display in each week - public static final int DAYS_PER_WEEK = 7; - // The size of the month name displayed above the week list - protected static final int MINI_MONTH_NAME_TEXT_SIZE = 18; - public static int LIST_TOP_OFFSET = -1; // so that the top line will be under the separator - protected int WEEK_MIN_VISIBLE_HEIGHT = 12; - protected int BOTTOM_BUFFER = 20; - protected int mSaturdayColor = 0; - protected int mSundayColor = 0; - protected int mDayNameColor = 0; - - // You can override these numbers to get a different appearance - protected int mNumWeeks = 6; - protected boolean mShowWeekNumber = false; - protected int mDaysPerWeek = 7; - - // These affect the scroll speed and feel - protected float mFriction = 1.0f; - - protected Context mContext; - protected Handler mHandler; - - protected float mMinimumFlingVelocity; - - // highlighted time - protected Time mSelectedDay = new Time(); - protected SimpleWeeksAdapter mAdapter; - protected ListView mListView; - protected ViewGroup mDayNamesHeader; - protected String[] mDayLabels; - - // disposable variable used for time calculations - protected Time mTempTime = new Time(); - - private static float mScale = 0; - // When the week starts; numbered like Time. (e.g. SUNDAY=0). - protected int mFirstDayOfWeek; - // The first day of the focus month - protected Time mFirstDayOfMonth = new Time(); - // The first day that is visible in the view - protected Time mFirstVisibleDay = new Time(); - // The name of the month to display - protected TextView mMonthName; - // The last name announced by accessibility - protected CharSequence mPrevMonthName; - // which month should be displayed/highlighted [0-11] - protected int mCurrentMonthDisplayed; - // used for tracking during a scroll - protected long mPreviousScrollPosition; - // used for tracking which direction the view is scrolling - protected boolean mIsScrollingUp = false; - // used for tracking what state listview is in - protected int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE; - // used for tracking what state listview is in - protected int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE; - - // This causes an update of the view at midnight - protected Runnable mTodayUpdater = new Runnable() { - @Override - public void run() { - Time midnight = new Time(mFirstVisibleDay.timezone); - midnight.setToNow(); - long currentMillis = midnight.toMillis(true); - - midnight.hour = 0; - midnight.minute = 0; - midnight.second = 0; - midnight.monthDay++; - long millisToMidnight = midnight.normalize(true) - currentMillis; - mHandler.postDelayed(this, millisToMidnight); - - if (mAdapter != null) { - mAdapter.notifyDataSetChanged(); - } - } - }; - - // This allows us to update our position when a day is tapped - protected DataSetObserver mObserver = new DataSetObserver() { - @Override - public void onChanged() { - Time day = mAdapter.getSelectedDay(); - if (day.year != mSelectedDay.year || day.yearDay != mSelectedDay.yearDay) { - goTo(day.toMillis(true), true, true, false); - } - } - }; - - public SimpleDayPickerFragment(long initialTime) { - goTo(initialTime, false, true, true); - mHandler = new Handler(); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mContext = activity; - String tz = Time.getCurrentTimezone(); - ViewConfiguration viewConfig = ViewConfiguration.get(activity); - mMinimumFlingVelocity = viewConfig.getScaledMinimumFlingVelocity(); - - // Ensure we're in the correct time zone - mSelectedDay.switchTimezone(tz); - mSelectedDay.normalize(true); - mFirstDayOfMonth.timezone = tz; - mFirstDayOfMonth.normalize(true); - mFirstVisibleDay.timezone = tz; - mFirstVisibleDay.normalize(true); - mTempTime.timezone = tz; - - Resources res = activity.getResources(); - mSaturdayColor = res.getColor(R.color.month_saturday); - mSundayColor = res.getColor(R.color.month_sunday); - mDayNameColor = res.getColor(R.color.month_day_names_color); - - // Adjust sizes for screen density - if (mScale == 0) { - mScale = activity.getResources().getDisplayMetrics().density; - if (mScale != 1) { - WEEK_MIN_VISIBLE_HEIGHT *= mScale; - BOTTOM_BUFFER *= mScale; - LIST_TOP_OFFSET *= mScale; - } - } - setUpAdapter(); - setListAdapter(mAdapter); - } - - /** - * Creates a new adapter if necessary and sets up its parameters. Override - * this method to provide a custom adapter. - */ - protected void setUpAdapter() { - HashMap weekParams = new HashMap(); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, mShowWeekNumber ? 1 : 0); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, - Time.getJulianDay(mSelectedDay.toMillis(false), mSelectedDay.gmtoff)); - if (mAdapter == null) { - mAdapter = new SimpleWeeksAdapter(getActivity(), weekParams); - mAdapter.registerDataSetObserver(mObserver); - } else { - mAdapter.updateParams(weekParams); - } - // refresh the view with the new parameters - mAdapter.notifyDataSetChanged(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - setUpListView(); - setUpHeader(); - - mMonthName = (TextView) getView().findViewById(R.id.month_name); - SimpleWeekView child = (SimpleWeekView) mListView.getChildAt(0); - if (child == null) { - return; - } - int julianDay = child.getFirstJulianDay(); - mFirstVisibleDay.setJulianDay(julianDay); - // set the title to the month of the second week - mTempTime.setJulianDay(julianDay + DAYS_PER_WEEK); - setMonthDisplayed(mTempTime, true); - } - - /** - * Sets up the strings to be used by the header. Override this method to use - * different strings or modify the view params. - */ - protected void setUpHeader() { - mDayLabels = new String[7]; - for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { - mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString(i, - DateUtils.LENGTH_SHORTEST).toUpperCase(); - } - } - - /** - * Sets all the required fields for the list view. Override this method to - * set a different list view behavior. - */ - protected void setUpListView() { - // Configure the listview - mListView = getListView(); - // Transparent background on scroll - mListView.setCacheColorHint(0); - // No dividers - mListView.setDivider(null); - // Items are clickable - mListView.setItemsCanFocus(true); - // The thumb gets in the way, so disable it - mListView.setFastScrollEnabled(false); - mListView.setVerticalScrollBarEnabled(false); - mListView.setOnScrollListener(this); - mListView.setFadingEdgeLength(0); - // Make the scrolling behavior nicer - mListView.setFriction(ViewConfiguration.getScrollFriction() * mFriction); - } - - @Override - public void onResume() { - super.onResume(); - setUpAdapter(); - doResumeUpdates(); - } - - @Override - public void onPause() { - super.onPause(); - mHandler.removeCallbacks(mTodayUpdater); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putLong(KEY_CURRENT_TIME, mSelectedDay.toMillis(true)); - } - - /** - * Updates the user preference fields. Override this to use a different - * preference space. - */ - protected void doResumeUpdates() { - // Get default week start based on locale, subtracting one for use with android Time. - Calendar cal = Calendar.getInstance(Locale.getDefault()); - mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1; - - mShowWeekNumber = false; - - updateHeader(); - goTo(mSelectedDay.toMillis(true), false, false, false); - mAdapter.setSelectedDay(mSelectedDay); - mTodayUpdater.run(); - } - - /** - * Fixes the day names header to provide correct spacing and updates the - * label text. Override this to set up a custom header. - */ - protected void updateHeader() { - TextView label = (TextView) mDayNamesHeader.findViewById(R.id.wk_label); - if (mShowWeekNumber) { - label.setVisibility(View.VISIBLE); - } else { - label.setVisibility(View.GONE); - } - int offset = mFirstDayOfWeek - 1; - for (int i = 1; i < 8; i++) { - label = (TextView) mDayNamesHeader.getChildAt(i); - if (i < mDaysPerWeek + 1) { - int position = (offset + i) % 7; - label.setText(mDayLabels[position]); - label.setVisibility(View.VISIBLE); - if (position == Time.SATURDAY) { - label.setTextColor(mSaturdayColor); - } else if (position == Time.SUNDAY) { - label.setTextColor(mSundayColor); - } else { - label.setTextColor(mDayNameColor); - } - } else { - label.setVisibility(View.GONE); - } - } - mDayNamesHeader.invalidate(); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.month_by_week, - container, false); - mDayNamesHeader = (ViewGroup) v.findViewById(R.id.day_names); - return v; - } - - /** - * Returns the UTC millis since epoch representation of the currently - * selected time. - * - * @return - */ - public long getSelectedTime() { - return mSelectedDay.toMillis(true); - } - - /** - * This moves to the specified time in the view. If the time is not already - * in range it will move the list so that the first of the month containing - * the time is at the top of the view. If the new time is already in view - * the list will not be scrolled unless forceScroll is true. This time may - * optionally be highlighted as selected as well. - * - * @param time The time to move to - * @param animate Whether to scroll to the given time or just redraw at the - * new location - * @param setSelected Whether to set the given time as selected - * @param forceScroll Whether to recenter even if the time is already - * visible - * @return Whether or not the view animated to the new location - */ - public boolean goTo(long time, boolean animate, boolean setSelected, boolean forceScroll) { - if (time == -1) { - Log.e(TAG, "time is invalid"); - return false; - } - - // Set the selected day - if (setSelected) { - mSelectedDay.set(time); - mSelectedDay.normalize(true); - } - - // If this view isn't returned yet we won't be able to load the lists - // current position, so return after setting the selected day. - if (!isResumed()) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "We're not visible yet"); - } - return false; - } - - mTempTime.set(time); - long millis = mTempTime.normalize(true); - // Get the week we're going to - // TODO push Util function into Calendar public api. - int position = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mTempTime.gmtoff), mFirstDayOfWeek); - - View child; - int i = 0; - int top = 0; - // Find a child that's completely in the view - do { - child = mListView.getChildAt(i++); - if (child == null) { - break; - } - top = child.getTop(); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "child at " + (i-1) + " has top " + top); - } - } while (top < 0); - - // Compute the first and last position visible - int firstPosition; - if (child != null) { - firstPosition = mListView.getPositionForView(child); - } else { - firstPosition = 0; - } - int lastPosition = firstPosition + mNumWeeks - 1; - if (top > BOTTOM_BUFFER) { - lastPosition--; - } - - if (setSelected) { - mAdapter.setSelectedDay(mSelectedDay); - } - - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "GoTo position " + position); - } - // Check if the selected day is now outside of our visible range - // and if so scroll to the month that contains it - if (position < firstPosition || position > lastPosition || forceScroll) { - mFirstDayOfMonth.set(mTempTime); - mFirstDayOfMonth.monthDay = 1; - millis = mFirstDayOfMonth.normalize(true); - setMonthDisplayed(mFirstDayOfMonth, true); - position = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mFirstDayOfMonth.gmtoff), mFirstDayOfWeek); - - mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING; - if (animate) { - mListView.smoothScrollToPositionFromTop( - position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION); - return true; - } else { - mListView.setSelectionFromTop(position, LIST_TOP_OFFSET); - // Perform any after scroll operations that are needed - onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE); - } - } else if (setSelected) { - // Otherwise just set the selection - setMonthDisplayed(mSelectedDay, true); - } - return false; - } - - /** - * Updates the title and selected month if the view has moved to a new - * month. - */ - @Override - public void onScroll( - AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - SimpleWeekView child = (SimpleWeekView)view.getChildAt(0); - if (child == null) { - return; - } - - // Figure out where we are - long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom(); - mFirstVisibleDay.setJulianDay(child.getFirstJulianDay()); - - // If we have moved since our last call update the direction - if (currScroll < mPreviousScrollPosition) { - mIsScrollingUp = true; - } else if (currScroll > mPreviousScrollPosition) { - mIsScrollingUp = false; - } else { - return; - } - - mPreviousScrollPosition = currScroll; - mPreviousScrollState = mCurrentScrollState; - - updateMonthHighlight(mListView); - } - - /** - * Figures out if the month being shown has changed and updates the - * highlight if needed - * - * @param view The ListView containing the weeks - */ - private void updateMonthHighlight(AbsListView view) { - SimpleWeekView child = (SimpleWeekView) view.getChildAt(0); - if (child == null) { - return; - } - - // Figure out where we are - int offset = child.getBottom() < WEEK_MIN_VISIBLE_HEIGHT ? 1 : 0; - // Use some hysteresis for checking which month to highlight. This - // causes the month to transition when two full weeks of a month are - // visible. - child = (SimpleWeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset); - - if (child == null) { - return; - } - - // Find out which month we're moving into - int month; - if (mIsScrollingUp) { - month = child.getFirstMonth(); - } else { - month = child.getLastMonth(); - } - - // And how it relates to our current highlighted month - int monthDiff; - if (mCurrentMonthDisplayed == 11 && month == 0) { - monthDiff = 1; - } else if (mCurrentMonthDisplayed == 0 && month == 11) { - monthDiff = -1; - } else { - monthDiff = month - mCurrentMonthDisplayed; - } - - // Only switch months if we're scrolling away from the currently - // selected month - if (monthDiff != 0) { - int julianDay = child.getFirstJulianDay(); - if (mIsScrollingUp) { - // Takes the start of the week - } else { - // Takes the start of the following week - julianDay += DAYS_PER_WEEK; - } - mTempTime.setJulianDay(julianDay); - setMonthDisplayed(mTempTime, false); - } - } - - /** - * Sets the month displayed at the top of this view based on time. Override - * to add custom events when the title is changed. - * - * @param time A day in the new focus month. - * @param updateHighlight TODO(epastern): - */ - protected void setMonthDisplayed(Time time, boolean updateHighlight) { - CharSequence oldMonth = mMonthName.getText(); - mMonthName.setText(Utils.formatMonthYear(mContext, time)); - mMonthName.invalidate(); - if (!TextUtils.equals(oldMonth, mMonthName.getText())) { - mMonthName.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); - } - mCurrentMonthDisplayed = time.month; - if (updateHighlight) { - mAdapter.updateFocusMonth(mCurrentMonthDisplayed); - } - } - - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - // use a post to prevent re-entering onScrollStateChanged before it - // exits - mScrollStateChangedRunnable.doScrollStateChange(view, scrollState); - } - - protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(); - - protected class ScrollStateRunnable implements Runnable { - private int mNewState; - - /** - * Sets up the runnable with a short delay in case the scroll state - * immediately changes again. - * - * @param view The list view that changed state - * @param scrollState The new state it changed to - */ - public void doScrollStateChange(AbsListView view, int scrollState) { - mHandler.removeCallbacks(this); - mNewState = scrollState; - mHandler.postDelayed(this, SCROLL_CHANGE_DELAY); - } - - public void run() { - mCurrentScrollState = mNewState; - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, - "new scroll state: " + mNewState + " old state: " + mPreviousScrollState); - } - // Fix the position after a scroll or a fling ends - if (mNewState == OnScrollListener.SCROLL_STATE_IDLE - && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) { - mPreviousScrollState = mNewState; - mAdapter.updateFocusMonth(mCurrentMonthDisplayed); - } else { - mPreviousScrollState = mNewState; - } - } - } -} diff --git a/src/com/android/calendar/month/SimpleWeekView.java b/src/com/android/calendar/month/SimpleWeekView.java deleted file mode 100644 index 4d0c09f4..00000000 --- a/src/com/android/calendar/month/SimpleWeekView.java +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Copyright (C) 2010 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.month; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.app.Service; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Paint.Style; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.view.MotionEvent; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; - -import java.security.InvalidParameterException; -import java.util.HashMap; - -/** - *

- * This is a dynamic view for drawing a single week. It can be configured to - * display the week number, start the week on a given day, or show a reduced - * number of days. It is intended for use as a single view within a ListView. - * See {@link SimpleWeeksAdapter} for usage. - *

- */ -public class SimpleWeekView extends View { - private static final String TAG = "MonthView"; - - /** - * These params can be passed into the view to control how it appears. - * {@link #VIEW_PARAMS_WEEK} is the only required field, though the default - * values are unlikely to fit most layouts correctly. - */ - /** - * This sets the height of this week in pixels - */ - public static final String VIEW_PARAMS_HEIGHT = "height"; - /** - * This specifies the position (or weeks since the epoch) of this week, - * calculated using {@link Utils#getWeeksSinceEpochFromJulianDay} - */ - public static final String VIEW_PARAMS_WEEK = "week"; - /** - * This sets one of the days in this view as selected {@link Time#SUNDAY} - * through {@link Time#SATURDAY}. - */ - public static final String VIEW_PARAMS_SELECTED_DAY = "selected_day"; - /** - * Which day the week should start on. {@link Time#SUNDAY} through - * {@link Time#SATURDAY}. - */ - public static final String VIEW_PARAMS_WEEK_START = "week_start"; - /** - * How many days to display at a time. Days will be displayed starting with - * {@link #mWeekStart}. - */ - public static final String VIEW_PARAMS_NUM_DAYS = "num_days"; - /** - * Which month is currently in focus, as defined by {@link Time#month} - * [0-11]. - */ - public static final String VIEW_PARAMS_FOCUS_MONTH = "focus_month"; - /** - * If this month should display week numbers. false if 0, true otherwise. - */ - public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num"; - - protected static int DEFAULT_HEIGHT = 32; - protected static int MIN_HEIGHT = 10; - protected static final int DEFAULT_SELECTED_DAY = -1; - protected static final int DEFAULT_WEEK_START = Time.SUNDAY; - protected static final int DEFAULT_NUM_DAYS = 7; - protected static final int DEFAULT_SHOW_WK_NUM = 0; - protected static final int DEFAULT_FOCUS_MONTH = -1; - - protected static int DAY_SEPARATOR_WIDTH = 1; - - protected static int MINI_DAY_NUMBER_TEXT_SIZE = 14; - protected static int MINI_WK_NUMBER_TEXT_SIZE = 12; - protected static int MINI_TODAY_NUMBER_TEXT_SIZE = 18; - protected static int MINI_TODAY_OUTLINE_WIDTH = 2; - protected static int WEEK_NUM_MARGIN_BOTTOM = 4; - - // used for scaling to the device density - protected static float mScale = 0; - - // affects the padding on the sides of this view - protected int mPadding = 0; - - protected Rect r = new Rect(); - protected Paint p = new Paint(); - protected Paint mMonthNumPaint; - protected Drawable mSelectedDayLine; - - // Cache the number strings so we don't have to recompute them each time - protected String[] mDayNumbers; - // Quick lookup for checking which days are in the focus month - protected boolean[] mFocusDay; - // Quick lookup for checking which days are in an odd month (to set a different background) - protected boolean[] mOddMonth; - // The Julian day of the first day displayed by this item - protected int mFirstJulianDay = -1; - // The month of the first day in this week - protected int mFirstMonth = -1; - // The month of the last day in this week - protected int mLastMonth = -1; - // The position of this week, equivalent to weeks since the week of Jan 1st, - // 1970 - protected int mWeek = -1; - // Quick reference to the width of this view, matches parent - protected int mWidth; - // The height this view should draw at in pixels, set by height param - protected int mHeight = DEFAULT_HEIGHT; - // Whether the week number should be shown - protected boolean mShowWeekNum = false; - // If this view contains the selected day - protected boolean mHasSelectedDay = false; - // If this view contains the today - protected boolean mHasToday = false; - // Which day is selected [0-6] or -1 if no day is selected - protected int mSelectedDay = DEFAULT_SELECTED_DAY; - // Which day is today [0-6] or -1 if no day is today - protected int mToday = DEFAULT_SELECTED_DAY; - // Which day of the week to start on [0-6] - protected int mWeekStart = DEFAULT_WEEK_START; - // How many days to display - protected int mNumDays = DEFAULT_NUM_DAYS; - // The number of days + a spot for week number if it is displayed - protected int mNumCells = mNumDays; - // The left edge of the selected day - protected int mSelectedLeft = -1; - // The right edge of the selected day - protected int mSelectedRight = -1; - // The timezone to display times/dates in (used for determining when Today - // is) - protected String mTimeZone = Time.getCurrentTimezone(); - - protected int mBGColor; - protected int mSelectedWeekBGColor; - protected int mFocusMonthColor; - protected int mOtherMonthColor; - protected int mDaySeparatorColor; - protected int mTodayOutlineColor; - protected int mWeekNumColor; - - public SimpleWeekView(Context context) { - super(context); - - Resources res = context.getResources(); - - mBGColor = res.getColor(R.color.month_bgcolor); - mSelectedWeekBGColor = res.getColor(R.color.month_selected_week_bgcolor); - mFocusMonthColor = res.getColor(R.color.month_mini_day_number); - mOtherMonthColor = res.getColor(R.color.month_other_month_day_number); - mDaySeparatorColor = res.getColor(R.color.month_grid_lines); - mTodayOutlineColor = res.getColor(R.color.mini_month_today_outline_color); - mWeekNumColor = res.getColor(R.color.month_week_num_color); - mSelectedDayLine = res.getDrawable(R.drawable.dayline_minical_holo_light); - - if (mScale == 0) { - mScale = context.getResources().getDisplayMetrics().density; - if (mScale != 1) { - DEFAULT_HEIGHT *= mScale; - MIN_HEIGHT *= mScale; - MINI_DAY_NUMBER_TEXT_SIZE *= mScale; - MINI_TODAY_NUMBER_TEXT_SIZE *= mScale; - MINI_TODAY_OUTLINE_WIDTH *= mScale; - WEEK_NUM_MARGIN_BOTTOM *= mScale; - DAY_SEPARATOR_WIDTH *= mScale; - MINI_WK_NUMBER_TEXT_SIZE *= mScale; - } - } - - // Sets up any standard paints that will be used - initView(); - } - - /** - * Sets all the parameters for displaying this week. The only required - * parameter is the week number. Other parameters have a default value and - * will only update if a new value is included, except for focus month, - * which will always default to no focus month if no value is passed in. See - * {@link #VIEW_PARAMS_HEIGHT} for more info on parameters. - * - * @param params A map of the new parameters, see - * {@link #VIEW_PARAMS_HEIGHT} - * @param tz The time zone this view should reference times in - */ - public void setWeekParams(HashMap params, String tz) { - if (!params.containsKey(VIEW_PARAMS_WEEK)) { - throw new InvalidParameterException("You must specify the week number for this view"); - } - setTag(params); - mTimeZone = tz; - // We keep the current value for any params not present - if (params.containsKey(VIEW_PARAMS_HEIGHT)) { - mHeight = params.get(VIEW_PARAMS_HEIGHT); - if (mHeight < MIN_HEIGHT) { - mHeight = MIN_HEIGHT; - } - } - if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) { - mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY); - } - mHasSelectedDay = mSelectedDay != -1; - if (params.containsKey(VIEW_PARAMS_NUM_DAYS)) { - mNumDays = params.get(VIEW_PARAMS_NUM_DAYS); - } - if (params.containsKey(VIEW_PARAMS_SHOW_WK_NUM)) { - if (params.get(VIEW_PARAMS_SHOW_WK_NUM) != 0) { - mShowWeekNum = true; - } else { - mShowWeekNum = false; - } - } - mNumCells = mShowWeekNum ? mNumDays + 1 : mNumDays; - - // Allocate space for caching the day numbers and focus values - mDayNumbers = new String[mNumCells]; - mFocusDay = new boolean[mNumCells]; - mOddMonth = new boolean[mNumCells]; - mWeek = params.get(VIEW_PARAMS_WEEK); - int julianMonday = Utils.getJulianMondayFromWeeksSinceEpoch(mWeek); - Time time = new Time(tz); - time.setJulianDay(julianMonday); - - // If we're showing the week number calculate it based on Monday - int i = 0; - if (mShowWeekNum) { - mDayNumbers[0] = Integer.toString(time.getWeekNumber()); - i++; - } - - if (params.containsKey(VIEW_PARAMS_WEEK_START)) { - mWeekStart = params.get(VIEW_PARAMS_WEEK_START); - } - - // Now adjust our starting day based on the start day of the week - // If the week is set to start on a Saturday the first week will be - // Dec 27th 1969 -Jan 2nd, 1970 - if (time.weekDay != mWeekStart) { - int diff = time.weekDay - mWeekStart; - if (diff < 0) { - diff += 7; - } - time.monthDay -= diff; - time.normalize(true); - } - - mFirstJulianDay = Time.getJulianDay(time.toMillis(true), time.gmtoff); - mFirstMonth = time.month; - - // Figure out what day today is - Time today = new Time(tz); - today.setToNow(); - mHasToday = false; - mToday = -1; - - int focusMonth = params.containsKey(VIEW_PARAMS_FOCUS_MONTH) ? params.get( - VIEW_PARAMS_FOCUS_MONTH) - : DEFAULT_FOCUS_MONTH; - - for (; i < mNumCells; i++) { - if (time.monthDay == 1) { - mFirstMonth = time.month; - } - mOddMonth [i] = (time.month %2) == 1; - if (time.month == focusMonth) { - mFocusDay[i] = true; - } else { - mFocusDay[i] = false; - } - if (time.year == today.year && time.yearDay == today.yearDay) { - mHasToday = true; - mToday = i; - } - mDayNumbers[i] = Integer.toString(time.monthDay++); - time.normalize(true); - } - // We do one extra add at the end of the loop, if that pushed us to a - // new month undo it - if (time.monthDay == 1) { - time.monthDay--; - time.normalize(true); - } - mLastMonth = time.month; - - updateSelectionPositions(); - } - - /** - * Sets up the text and style properties for painting. Override this if you - * want to use a different paint. - */ - protected void initView() { - p.setFakeBoldText(false); - p.setAntiAlias(true); - p.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); - p.setStyle(Style.FILL); - - mMonthNumPaint = new Paint(); - mMonthNumPaint.setFakeBoldText(true); - mMonthNumPaint.setAntiAlias(true); - mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); - mMonthNumPaint.setColor(mFocusMonthColor); - mMonthNumPaint.setStyle(Style.FILL); - mMonthNumPaint.setTextAlign(Align.CENTER); - } - - /** - * Returns the month of the first day in this week - * - * @return The month the first day of this view is in - */ - public int getFirstMonth() { - return mFirstMonth; - } - - /** - * Returns the month of the last day in this week - * - * @return The month the last day of this view is in - */ - public int getLastMonth() { - return mLastMonth; - } - - /** - * Returns the julian day of the first day in this view. - * - * @return The julian day of the first day in the view. - */ - public int getFirstJulianDay() { - return mFirstJulianDay; - } - - /** - * Calculates the day that the given x position is in, accounting for week - * number. Returns a Time referencing that day or null if - * - * @param x The x position of the touch event - * @return A time object for the tapped day or null if the position wasn't - * in a day - */ - public Time getDayFromLocation(float x) { - int dayStart = mShowWeekNum ? (mWidth - mPadding * 2) / mNumCells + mPadding : mPadding; - if (x < dayStart || x > mWidth - mPadding) { - return null; - } - // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels - int dayPosition = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)); - int day = mFirstJulianDay + dayPosition; - - Time time = new Time(mTimeZone); - if (mWeek == 0) { - // This week is weird... - if (day < Time.EPOCH_JULIAN_DAY) { - day++; - } else if (day == Time.EPOCH_JULIAN_DAY) { - time.set(1, 0, 1970); - time.normalize(true); - return time; - } - } - - time.setJulianDay(day); - return time; - } - - @Override - protected void onDraw(Canvas canvas) { - drawBackground(canvas); - drawWeekNums(canvas); - drawDaySeparators(canvas); - } - - /** - * This draws the selection highlight if a day is selected in this week. - * Override this method if you wish to have a different background drawn. - * - * @param canvas The canvas to draw on - */ - protected void drawBackground(Canvas canvas) { - if (mHasSelectedDay) { - p.setColor(mSelectedWeekBGColor); - p.setStyle(Style.FILL); - } else { - return; - } - r.top = 1; - r.bottom = mHeight - 1; - r.left = mPadding; - r.right = mSelectedLeft; - canvas.drawRect(r, p); - r.left = mSelectedRight; - r.right = mWidth - mPadding; - canvas.drawRect(r, p); - } - - /** - * Draws the week and month day numbers for this week. Override this method - * if you need different placement. - * - * @param canvas The canvas to draw on - */ - protected void drawWeekNums(Canvas canvas) { - int y = ((mHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2) - DAY_SEPARATOR_WIDTH; - int nDays = mNumCells; - - int i = 0; - int divisor = 2 * nDays; - if (mShowWeekNum) { - p.setTextSize(MINI_WK_NUMBER_TEXT_SIZE); - p.setStyle(Style.FILL); - p.setTextAlign(Align.CENTER); - p.setAntiAlias(true); - p.setColor(mWeekNumColor); - int x = (mWidth - mPadding * 2) / divisor + mPadding; - canvas.drawText(mDayNumbers[0], x, y, p); - i++; - } - - boolean isFocusMonth = mFocusDay[i]; - mMonthNumPaint.setColor(isFocusMonth ? mFocusMonthColor : mOtherMonthColor); - mMonthNumPaint.setFakeBoldText(false); - for (; i < nDays; i++) { - if (mFocusDay[i] != isFocusMonth) { - isFocusMonth = mFocusDay[i]; - mMonthNumPaint.setColor(isFocusMonth ? mFocusMonthColor : mOtherMonthColor); - } - if (mHasToday && mToday == i) { - mMonthNumPaint.setTextSize(MINI_TODAY_NUMBER_TEXT_SIZE); - mMonthNumPaint.setFakeBoldText(true); - } - int x = (2 * i + 1) * (mWidth - mPadding * 2) / (divisor) + mPadding; - canvas.drawText(mDayNumbers[i], x, y, mMonthNumPaint); - if (mHasToday && mToday == i) { - mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); - mMonthNumPaint.setFakeBoldText(false); - } - } - } - - /** - * Draws a horizontal line for separating the weeks. Override this method if - * you want custom separators. - * - * @param canvas The canvas to draw on - */ - protected void drawDaySeparators(Canvas canvas) { - if (mHasSelectedDay) { - r.top = 1; - r.bottom = mHeight - 1; - r.left = mSelectedLeft + 1; - r.right = mSelectedRight - 1; - p.setStrokeWidth(MINI_TODAY_OUTLINE_WIDTH); - p.setStyle(Style.STROKE); - p.setColor(mTodayOutlineColor); - canvas.drawRect(r, p); - } - if (mShowWeekNum) { - p.setColor(mDaySeparatorColor); - p.setStrokeWidth(DAY_SEPARATOR_WIDTH); - - int x = (mWidth - mPadding * 2) / mNumCells + mPadding; - canvas.drawLine(x, 0, x, mHeight, p); - } - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - mWidth = w; - updateSelectionPositions(); - } - - /** - * This calculates the positions for the selected day lines. - */ - protected void updateSelectionPositions() { - if (mHasSelectedDay) { - int selectedPosition = mSelectedDay - mWeekStart; - if (selectedPosition < 0) { - selectedPosition += 7; - } - if (mShowWeekNum) { - selectedPosition++; - } - mSelectedLeft = selectedPosition * (mWidth - mPadding * 2) / mNumCells - + mPadding; - mSelectedRight = (selectedPosition + 1) * (mWidth - mPadding * 2) / mNumCells - + mPadding; - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight); - } - - @Override - public boolean onHoverEvent(MotionEvent event) { - Context context = getContext(); - // only send accessibility events if accessibility and exploration are - // on. - AccessibilityManager am = (AccessibilityManager) context - .getSystemService(Service.ACCESSIBILITY_SERVICE); - if (!am.isEnabled() || !am.isTouchExplorationEnabled()) { - return super.onHoverEvent(event); - } - if (event.getAction() != MotionEvent.ACTION_HOVER_EXIT) { - Time hover = getDayFromLocation(event.getX()); - if (hover != null - && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) != 0)) { - Long millis = hover.toMillis(true); - String date = Utils.formatDateRange(context, millis, millis, - DateUtils.FORMAT_SHOW_DATE); - AccessibilityEvent accessEvent = - AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); - accessEvent.getText().add(date); - sendAccessibilityEventUnchecked(accessEvent); - mLastHoverTime = hover; - } - } - return true; - } - - Time mLastHoverTime = null; -} \ No newline at end of file diff --git a/src/com/android/calendar/month/SimpleWeeksAdapter.java b/src/com/android/calendar/month/SimpleWeeksAdapter.java deleted file mode 100644 index d29b2622..00000000 --- a/src/com/android/calendar/month/SimpleWeeksAdapter.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2010 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.month; - -// TODO Remove calendar imports when the required methods have been -// refactored into the public api -import com.android.calendar.CalendarController; -import com.android.calendar.Utils; - -import android.content.Context; -import android.text.format.Time; -import android.util.Log; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.ViewGroup; -import android.widget.AbsListView.LayoutParams; -import android.widget.BaseAdapter; -import android.widget.ListView; - -import java.util.Calendar; -import java.util.HashMap; -import java.util.Locale; - -/** - *

- * This is a specialized adapter for creating a list of weeks with selectable - * days. It can be configured to display the week number, start the week on a - * given day, show a reduced number of days, or display an arbitrary number of - * weeks at a time. See {@link SimpleDayPickerFragment} for usage. - *

- */ -public class SimpleWeeksAdapter extends BaseAdapter implements OnTouchListener { - - private static final String TAG = "MonthByWeek"; - - /** - * The number of weeks to display at a time. - */ - public static final String WEEK_PARAMS_NUM_WEEKS = "num_weeks"; - /** - * Which month should be in focus currently. - */ - public static final String WEEK_PARAMS_FOCUS_MONTH = "focus_month"; - /** - * Whether the week number should be shown. Non-zero to show them. - */ - public static final String WEEK_PARAMS_SHOW_WEEK = "week_numbers"; - /** - * Which day the week should start on. {@link Time#SUNDAY} through - * {@link Time#SATURDAY}. - */ - public static final String WEEK_PARAMS_WEEK_START = "week_start"; - /** - * The Julian day to highlight as selected. - */ - public static final String WEEK_PARAMS_JULIAN_DAY = "selected_day"; - /** - * How many days of the week to display [1-7]. - */ - public static final String WEEK_PARAMS_DAYS_PER_WEEK = "days_per_week"; - - protected static final int WEEK_COUNT = CalendarController.MAX_CALENDAR_WEEK - - CalendarController.MIN_CALENDAR_WEEK; - protected static int DEFAULT_NUM_WEEKS = 6; - protected static int DEFAULT_MONTH_FOCUS = 0; - protected static int DEFAULT_DAYS_PER_WEEK = 7; - protected static int DEFAULT_WEEK_HEIGHT = 32; - protected static int WEEK_7_OVERHANG_HEIGHT = 7; - - protected static float mScale = 0; - protected Context mContext; - // The day to highlight as selected - protected Time mSelectedDay; - // The week since 1970 that the selected day is in - protected int mSelectedWeek; - // When the week starts; numbered like Time. (e.g. SUNDAY=0). - protected int mFirstDayOfWeek; - protected boolean mShowWeekNumber = false; - protected GestureDetector mGestureDetector; - protected int mNumWeeks = DEFAULT_NUM_WEEKS; - protected int mDaysPerWeek = DEFAULT_DAYS_PER_WEEK; - protected int mFocusMonth = DEFAULT_MONTH_FOCUS; - - public SimpleWeeksAdapter(Context context, HashMap params) { - mContext = context; - - // Get default week start based on locale, subtracting one for use with android Time. - Calendar cal = Calendar.getInstance(Locale.getDefault()); - mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1; - - if (mScale == 0) { - mScale = context.getResources().getDisplayMetrics().density; - if (mScale != 1) { - WEEK_7_OVERHANG_HEIGHT *= mScale; - } - } - init(); - updateParams(params); - } - - /** - * Set up the gesture detector and selected time - */ - protected void init() { - mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener()); - mSelectedDay = new Time(); - mSelectedDay.setToNow(); - } - - /** - * Parse the parameters and set any necessary fields. See - * {@link #WEEK_PARAMS_NUM_WEEKS} for parameter details. - * - * @param params A list of parameters for this adapter - */ - public void updateParams(HashMap params) { - if (params == null) { - Log.e(TAG, "WeekParameters are null! Cannot update adapter."); - return; - } - if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) { - mFocusMonth = params.get(WEEK_PARAMS_FOCUS_MONTH); - } - if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) { - mNumWeeks = params.get(WEEK_PARAMS_NUM_WEEKS); - } - if (params.containsKey(WEEK_PARAMS_SHOW_WEEK)) { - mShowWeekNumber = params.get(WEEK_PARAMS_SHOW_WEEK) != 0; - } - if (params.containsKey(WEEK_PARAMS_WEEK_START)) { - mFirstDayOfWeek = params.get(WEEK_PARAMS_WEEK_START); - } - if (params.containsKey(WEEK_PARAMS_JULIAN_DAY)) { - int julianDay = params.get(WEEK_PARAMS_JULIAN_DAY); - mSelectedDay.setJulianDay(julianDay); - mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay(julianDay, mFirstDayOfWeek); - } - if (params.containsKey(WEEK_PARAMS_DAYS_PER_WEEK)) { - mDaysPerWeek = params.get(WEEK_PARAMS_DAYS_PER_WEEK); - } - refresh(); - } - - /** - * Updates the selected day and related parameters. - * - * @param selectedTime The time to highlight - */ - public void setSelectedDay(Time selectedTime) { - mSelectedDay.set(selectedTime); - long millis = mSelectedDay.normalize(true); - mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek); - notifyDataSetChanged(); - } - - /** - * Returns the currently highlighted day - * - * @return - */ - public Time getSelectedDay() { - return mSelectedDay; - } - - /** - * updates any config options that may have changed and refreshes the view - */ - protected void refresh() { - notifyDataSetChanged(); - } - - @Override - public int getCount() { - return WEEK_COUNT; - } - - @Override - public Object getItem(int position) { - return null; - } - - @Override - public long getItemId(int position) { - return position; - } - - @SuppressWarnings("unchecked") - @Override - public View getView(int position, View convertView, ViewGroup parent) { - SimpleWeekView v; - HashMap drawingParams = null; - if (convertView != null) { - v = (SimpleWeekView) convertView; - // We store the drawing parameters in the view so it can be recycled - drawingParams = (HashMap) v.getTag(); - } else { - v = new SimpleWeekView(mContext); - // Set up the new view - LayoutParams params = new LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - v.setLayoutParams(params); - v.setClickable(true); - v.setOnTouchListener(this); - } - if (drawingParams == null) { - drawingParams = new HashMap(); - } - drawingParams.clear(); - - int selectedDay = -1; - if (mSelectedWeek == position) { - selectedDay = mSelectedDay.weekDay; - } - - // pass in all the view parameters - drawingParams.put(SimpleWeekView.VIEW_PARAMS_HEIGHT, - (parent.getHeight() - WEEK_7_OVERHANG_HEIGHT) / mNumWeeks); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, mShowWeekNumber ? 1 : 0); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth); - v.setWeekParams(drawingParams, mSelectedDay.timezone); - v.invalidate(); - - return v; - } - - /** - * Changes which month is in focus and updates the view. - * - * @param month The month to show as in focus [0-11] - */ - public void updateFocusMonth(int month) { - mFocusMonth = month; - notifyDataSetChanged(); - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - if (mGestureDetector.onTouchEvent(event)) { - SimpleWeekView view = (SimpleWeekView) v; - Time day = ((SimpleWeekView)v).getDayFromLocation(event.getX()); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Touched day at Row=" + view.mWeek + " day=" + day.toString()); - } - if (day != null) { - onDayTapped(day); - } - return true; - } - return false; - } - - /** - * Maintains the same hour/min/sec but moves the day to the tapped day. - * - * @param day The day that was tapped - */ - protected void onDayTapped(Time day) { - day.hour = mSelectedDay.hour; - day.minute = mSelectedDay.minute; - day.second = mSelectedDay.second; - setSelectedDay(day); - } - - - /** - * This is here so we can identify single tap events and set the selected - * day correctly - */ - protected class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { - @Override - public boolean onSingleTapUp(MotionEvent e) { - return true; - } - } - - ListView mListView; - - public void setListView(ListView lv) { - mListView = lv; - } -} diff --git a/src/com/android/calendar/widget/CalendarAppWidgetModel.java b/src/com/android/calendar/widget/CalendarAppWidgetModel.java deleted file mode 100644 index a989e18b..00000000 --- a/src/com/android/calendar/widget/CalendarAppWidgetModel.java +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright (C) 2010 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.widget; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.content.Context; -import android.database.Cursor; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.View; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.TimeZone; - -class CalendarAppWidgetModel { - private static final String TAG = CalendarAppWidgetModel.class.getSimpleName(); - private static final boolean LOGD = false; - - private String mHomeTZName; - private boolean mShowTZ; - /** - * {@link RowInfo} is a class that represents a single row in the widget. It - * is actually only a pointer to either a {@link DayInfo} or an - * {@link EventInfo} instance, since a row in the widget might be either a - * day header or an event. - */ - static class RowInfo { - static final int TYPE_DAY = 0; - static final int TYPE_MEETING = 1; - - /** - * mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING) - */ - final int mType; - - /** - * If mType is TYPE_DAY, then mData is the index into day infos. - * Otherwise mType is TYPE_MEETING and mData is the index into event - * infos. - */ - final int mIndex; - - RowInfo(int type, int index) { - mType = type; - mIndex = index; - } - } - - /** - * {@link EventInfo} is a class that represents an event in the widget. It - * contains all of the data necessary to display that event, including the - * properly localized strings and visibility settings. - */ - static class EventInfo { - int visibWhen; // Visibility value for When textview (View.GONE or View.VISIBLE) - String when; - int visibWhere; // Visibility value for Where textview (View.GONE or View.VISIBLE) - String where; - int visibTitle; // Visibility value for Title textview (View.GONE or View.VISIBLE) - String title; - int selfAttendeeStatus; - - long id; - long start; - long end; - boolean allDay; - int color; - - public EventInfo() { - visibWhen = View.GONE; - visibWhere = View.GONE; - visibTitle = View.GONE; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("EventInfo [visibTitle="); - builder.append(visibTitle); - builder.append(", title="); - builder.append(title); - builder.append(", visibWhen="); - builder.append(visibWhen); - builder.append(", id="); - builder.append(id); - builder.append(", when="); - builder.append(when); - builder.append(", visibWhere="); - builder.append(visibWhere); - builder.append(", where="); - builder.append(where); - builder.append(", color="); - builder.append(String.format("0x%x", color)); - builder.append(", selfAttendeeStatus="); - builder.append(selfAttendeeStatus); - builder.append("]"); - return builder.toString(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (allDay ? 1231 : 1237); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (int) (end ^ (end >>> 32)); - result = prime * result + (int) (start ^ (start >>> 32)); - result = prime * result + ((title == null) ? 0 : title.hashCode()); - result = prime * result + visibTitle; - result = prime * result + visibWhen; - result = prime * result + visibWhere; - result = prime * result + ((when == null) ? 0 : when.hashCode()); - result = prime * result + ((where == null) ? 0 : where.hashCode()); - result = prime * result + color; - result = prime * result + selfAttendeeStatus; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - EventInfo other = (EventInfo) obj; - if (id != other.id) - return false; - if (allDay != other.allDay) - return false; - if (end != other.end) - return false; - if (start != other.start) - return false; - if (title == null) { - if (other.title != null) - return false; - } else if (!title.equals(other.title)) - return false; - if (visibTitle != other.visibTitle) - return false; - if (visibWhen != other.visibWhen) - return false; - if (visibWhere != other.visibWhere) - return false; - if (when == null) { - if (other.when != null) - return false; - } else if (!when.equals(other.when)) { - return false; - } - if (where == null) { - if (other.where != null) - return false; - } else if (!where.equals(other.where)) { - return false; - } - if (color != other.color) { - return false; - } - if (selfAttendeeStatus != other.selfAttendeeStatus) { - return false; - } - return true; - } - } - - /** - * {@link DayInfo} is a class that represents a day header in the widget. It - * contains all of the data necessary to display that day header, including - * the properly localized string. - */ - static class DayInfo { - - /** The Julian day */ - final int mJulianDay; - - /** The string representation of this day header, to be displayed */ - final String mDayLabel; - - DayInfo(int julianDay, String label) { - mJulianDay = julianDay; - mDayLabel = label; - } - - @Override - public String toString() { - return mDayLabel; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mDayLabel == null) ? 0 : mDayLabel.hashCode()); - result = prime * result + mJulianDay; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DayInfo other = (DayInfo) obj; - if (mDayLabel == null) { - if (other.mDayLabel != null) - return false; - } else if (!mDayLabel.equals(other.mDayLabel)) - return false; - if (mJulianDay != other.mJulianDay) - return false; - return true; - } - - } - - final List mRowInfos; - final List mEventInfos; - final List mDayInfos; - final Context mContext; - final long mNow; - final int mTodayJulianDay; - final int mMaxJulianDay; - - public CalendarAppWidgetModel(Context context, String timeZone) { - mNow = System.currentTimeMillis(); - Time time = new Time(timeZone); - time.setToNow(); // This is needed for gmtoff to be set - mTodayJulianDay = Time.getJulianDay(mNow, time.gmtoff); - mMaxJulianDay = mTodayJulianDay + CalendarAppWidgetService.MAX_DAYS - 1; - mEventInfos = new ArrayList(50); - mRowInfos = new ArrayList(50); - mDayInfos = new ArrayList(8); - mContext = context; - } - - public void buildFromCursor(Cursor cursor, String timeZone) { - final Time recycle = new Time(timeZone); - final ArrayList> mBuckets = - new ArrayList>(CalendarAppWidgetService.MAX_DAYS); - for (int i = 0; i < CalendarAppWidgetService.MAX_DAYS; i++) { - mBuckets.add(new LinkedList()); - } - recycle.setToNow(); - mShowTZ = !TextUtils.equals(timeZone, Time.getCurrentTimezone()); - if (mShowTZ) { - mHomeTZName = TimeZone.getTimeZone(timeZone).getDisplayName(recycle.isDst != 0, - TimeZone.SHORT); - } - - cursor.moveToPosition(-1); - String tz = Utils.getTimeZone(mContext, null); - while (cursor.moveToNext()) { - final int rowId = cursor.getPosition(); - final long eventId = cursor.getLong(CalendarAppWidgetService.INDEX_EVENT_ID); - final boolean allDay = cursor.getInt(CalendarAppWidgetService.INDEX_ALL_DAY) != 0; - long start = cursor.getLong(CalendarAppWidgetService.INDEX_BEGIN); - long end = cursor.getLong(CalendarAppWidgetService.INDEX_END); - final String title = cursor.getString(CalendarAppWidgetService.INDEX_TITLE); - final String location = - cursor.getString(CalendarAppWidgetService.INDEX_EVENT_LOCATION); - // we don't compute these ourselves because it seems to produce the - // wrong endDay for all day events - final int startDay = cursor.getInt(CalendarAppWidgetService.INDEX_START_DAY); - final int endDay = cursor.getInt(CalendarAppWidgetService.INDEX_END_DAY); - final int color = cursor.getInt(CalendarAppWidgetService.INDEX_COLOR); - final int selfStatus = cursor - .getInt(CalendarAppWidgetService.INDEX_SELF_ATTENDEE_STATUS); - - // Adjust all-day times into local timezone - if (allDay) { - start = Utils.convertAlldayUtcToLocal(recycle, start, tz); - end = Utils.convertAlldayUtcToLocal(recycle, end, tz); - } - - if (LOGD) { - Log.d(TAG, "Row #" + rowId + " allDay:" + allDay + " start:" + start - + " end:" + end + " eventId:" + eventId); - } - - // we might get some extra events when querying, in order to - // deal with all-day events - if (end < mNow) { - continue; - } - - int i = mEventInfos.size(); - mEventInfos.add(populateEventInfo(eventId, allDay, start, end, startDay, endDay, title, - location, color, selfStatus)); - // populate the day buckets that this event falls into - int from = Math.max(startDay, mTodayJulianDay); - int to = Math.min(endDay, mMaxJulianDay); - for (int day = from; day <= to; day++) { - LinkedList bucket = mBuckets.get(day - mTodayJulianDay); - RowInfo rowInfo = new RowInfo(RowInfo.TYPE_MEETING, i); - if (allDay) { - bucket.addFirst(rowInfo); - } else { - bucket.add(rowInfo); - } - } - } - - int day = mTodayJulianDay; - int count = 0; - for (LinkedList bucket : mBuckets) { - if (!bucket.isEmpty()) { - // We don't show day header in today - if (day != mTodayJulianDay) { - final DayInfo dayInfo = populateDayInfo(day, recycle); - // Add the day header - final int dayIndex = mDayInfos.size(); - mDayInfos.add(dayInfo); - mRowInfos.add(new RowInfo(RowInfo.TYPE_DAY, dayIndex)); - } - - // Add the event row infos - mRowInfos.addAll(bucket); - count += bucket.size(); - } - day++; - if (count >= CalendarAppWidgetService.EVENT_MIN_COUNT) { - break; - } - } - } - - private EventInfo populateEventInfo(long eventId, boolean allDay, long start, long end, - int startDay, int endDay, String title, String location, int color, int selfStatus) { - EventInfo eventInfo = new EventInfo(); - - // Compute a human-readable string for the start time of the event - StringBuilder whenString = new StringBuilder(); - int visibWhen; - int flags = DateUtils.FORMAT_ABBREV_ALL; - visibWhen = View.VISIBLE; - if (allDay) { - flags |= DateUtils.FORMAT_SHOW_DATE; - whenString.append(Utils.formatDateRange(mContext, start, end, flags)); - } else { - flags |= DateUtils.FORMAT_SHOW_TIME; - if (DateFormat.is24HourFormat(mContext)) { - flags |= DateUtils.FORMAT_24HOUR; - } - if (endDay > startDay) { - flags |= DateUtils.FORMAT_SHOW_DATE; - } - whenString.append(Utils.formatDateRange(mContext, start, end, flags)); - - if (mShowTZ) { - whenString.append(" ").append(mHomeTZName); - } - } - eventInfo.id = eventId; - eventInfo.start = start; - eventInfo.end = end; - eventInfo.allDay = allDay; - eventInfo.when = whenString.toString(); - eventInfo.visibWhen = visibWhen; - eventInfo.color = color; - eventInfo.selfAttendeeStatus = selfStatus; - - // What - if (TextUtils.isEmpty(title)) { - eventInfo.title = mContext.getString(R.string.no_title_label); - } else { - eventInfo.title = title; - } - eventInfo.visibTitle = View.VISIBLE; - - // Where - if (!TextUtils.isEmpty(location)) { - eventInfo.visibWhere = View.VISIBLE; - eventInfo.where = location; - } else { - eventInfo.visibWhere = View.GONE; - } - return eventInfo; - } - - private DayInfo populateDayInfo(int julianDay, Time recycle) { - long millis = recycle.setJulianDay(julianDay); - int flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE; - - String label; - if (julianDay == mTodayJulianDay + 1) { - label = mContext.getString(R.string.agenda_tomorrow, - Utils.formatDateRange(mContext, millis, millis, flags).toString()); - } else { - flags |= DateUtils.FORMAT_SHOW_WEEKDAY; - label = Utils.formatDateRange(mContext, millis, millis, flags); - } - return new DayInfo(julianDay, label); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("\nCalendarAppWidgetModel [eventInfos="); - builder.append(mEventInfos); - builder.append("]"); - return builder.toString(); - } -} \ No newline at end of file diff --git a/src/com/android/calendar/widget/CalendarAppWidgetProvider.java b/src/com/android/calendar/widget/CalendarAppWidgetProvider.java deleted file mode 100644 index 3a69efd3..00000000 --- a/src/com/android/calendar/widget/CalendarAppWidgetProvider.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * 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.widget; - -import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY; -import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; -import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProvider; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.provider.CalendarContract; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.widget.RemoteViews; - -import com.android.calendar.AllInOneActivity; -import com.android.calendar.EventInfoActivity; -import com.android.calendar.R; -import com.android.calendar.Utils; - -/** - * Simple widget to show next upcoming calendar event. - */ -public class CalendarAppWidgetProvider extends AppWidgetProvider { - static final String TAG = "CalendarAppWidgetProvider"; - static final boolean LOGD = false; - - // TODO Move these to Calendar.java - static final String EXTRA_EVENT_IDS = "com.android.calendar.EXTRA_EVENT_IDS"; - - /** - * {@inheritDoc} - */ - @Override - public void onReceive(Context context, Intent intent) { - // Handle calendar-specific updates ourselves because they might be - // coming in without extras, which AppWidgetProvider then blocks. - final String action = intent.getAction(); - if (LOGD) - Log.d(TAG, "AppWidgetProvider got the intent: " + intent.toString()); - if (Utils.getWidgetUpdateAction(context).equals(action)) { - AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); - performUpdate(context, appWidgetManager, - appWidgetManager.getAppWidgetIds(getComponentName(context)), - null /* no eventIds */); - } else if (action != null && (action.equals(Intent.ACTION_PROVIDER_CHANGED) - || action.equals(Intent.ACTION_TIME_CHANGED) - || action.equals(Intent.ACTION_TIMEZONE_CHANGED) - || action.equals(Intent.ACTION_DATE_CHANGED) - || action.equals(Utils.getWidgetScheduledUpdateAction(context)))) { - Intent service = new Intent(context, CalendarAppWidgetService.class); - context.startService(service); - } else { - super.onReceive(context, intent); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onDisabled(Context context) { - // Unsubscribe from all AlarmManager updates - AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - PendingIntent pendingUpdate = getUpdateIntent(context); - am.cancel(pendingUpdate); - } - - /** - * {@inheritDoc} - */ - @Override - public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { - performUpdate(context, appWidgetManager, appWidgetIds, null /* no eventIds */); - } - - - /** - * Build {@link ComponentName} describing this specific - * {@link AppWidgetProvider} - */ - static ComponentName getComponentName(Context context) { - return new ComponentName(context, CalendarAppWidgetProvider.class); - } - - /** - * Process and push out an update for the given appWidgetIds. This call - * actually fires an intent to start {@link CalendarAppWidgetService} as a - * background service which handles the actual update, to prevent ANR'ing - * during database queries. - * - * @param context Context to use when starting {@link CalendarAppWidgetService}. - * @param appWidgetIds List of specific appWidgetIds to update, or null for - * all. - * @param changedEventIds Specific events known to be changed. If present, - * we use it to decide if an update is necessary. - */ - private void performUpdate(Context context, - AppWidgetManager appWidgetManager, int[] appWidgetIds, - long[] changedEventIds) { - // Launch over to service so it can perform update - for (int appWidgetId : appWidgetIds) { - if (LOGD) Log.d(TAG, "Building widget update..."); - Intent updateIntent = new Intent(context, CalendarAppWidgetService.class); - updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); - if (changedEventIds != null) { - updateIntent.putExtra(EXTRA_EVENT_IDS, changedEventIds); - } - updateIntent.setData(Uri.parse(updateIntent.toUri(Intent.URI_INTENT_SCHEME))); - - RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget); - // Calendar header - Time time = new Time(Utils.getTimeZone(context, null)); - time.setToNow(); - long millis = time.toMillis(true); - final String dayOfWeek = DateUtils.getDayOfWeekString(time.weekDay + 1, - DateUtils.LENGTH_MEDIUM); - final String date = Utils.formatDateRange(context, millis, millis, - DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE - | DateUtils.FORMAT_NO_YEAR); - views.setTextViewText(R.id.day_of_week, dayOfWeek); - views.setTextViewText(R.id.date, date); - // Attach to list of events - views.setRemoteAdapter(appWidgetId, R.id.events_list, updateIntent); - appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.events_list); - - - // Launch calendar app when the user taps on the header - final Intent launchCalendarIntent = new Intent(Intent.ACTION_VIEW); - launchCalendarIntent.setClass(context, AllInOneActivity.class); - launchCalendarIntent - .setData(Uri.parse("content://com.android.calendar/time/" + millis)); - final PendingIntent launchCalendarPendingIntent = PendingIntent.getActivity( - context, 0 /* no requestCode */, launchCalendarIntent, 0 /* no flags */); - views.setOnClickPendingIntent(R.id.header, launchCalendarPendingIntent); - - // Each list item will call setOnClickExtra() to let the list know - // which item - // is selected by a user. - final PendingIntent updateEventIntent = getLaunchPendingIntentTemplate(context); - views.setPendingIntentTemplate(R.id.events_list, updateEventIntent); - - appWidgetManager.updateAppWidget(appWidgetId, views); - } - } - - /** - * Build the {@link PendingIntent} used to trigger an update of all calendar - * widgets. Uses {@link Utils#getWidgetScheduledUpdateAction(Context)} to - * directly target all widgets instead of using - * {@link AppWidgetManager#EXTRA_APPWIDGET_IDS}. - * - * @param context Context to use when building broadcast. - */ - static PendingIntent getUpdateIntent(Context context) { - Intent intent = new Intent(Utils.getWidgetScheduledUpdateAction(context)); - intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE); - return PendingIntent.getBroadcast(context, 0 /* no requestCode */, intent, - 0 /* no flags */); - } - - /** - * Build a {@link PendingIntent} to launch the Calendar app. This should be used - * in combination with {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)}. - */ - static PendingIntent getLaunchPendingIntentTemplate(Context context) { - Intent launchIntent = new Intent(); - launchIntent.setAction(Intent.ACTION_VIEW); - launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | - Intent.FLAG_ACTIVITY_TASK_ON_HOME); - launchIntent.setClass(context, AllInOneActivity.class); - return PendingIntent.getActivity(context, 0 /* no requestCode */, launchIntent, - PendingIntent.FLAG_UPDATE_CURRENT); - } - - /** - * Build an {@link Intent} available as FillInIntent to launch the Calendar app. - * This should be used in combination with - * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}. - * If the go to time is 0, then calendar will be launched without a starting time. - * - * @param goToTime time that calendar should take the user to, or 0 to - * indicate no specific start time. - */ - static Intent getLaunchFillInIntent(Context context, long id, long start, long end, - boolean allDay) { - final Intent fillInIntent = new Intent(); - String dataString = "content://com.android.calendar/events"; - if (id != 0) { - fillInIntent.putExtra(Utils.INTENT_KEY_DETAIL_VIEW, true); - fillInIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | - Intent.FLAG_ACTIVITY_TASK_ON_HOME); - - dataString += "/" + id; - // If we have an event id - start the event info activity - fillInIntent.setClass(context, EventInfoActivity.class); - } else { - // If we do not have an event id - start AllInOne - fillInIntent.setClass(context, AllInOneActivity.class); - } - Uri data = Uri.parse(dataString); - fillInIntent.setData(data); - fillInIntent.putExtra(EXTRA_EVENT_BEGIN_TIME, start); - fillInIntent.putExtra(EXTRA_EVENT_END_TIME, end); - fillInIntent.putExtra(EXTRA_EVENT_ALL_DAY, allDay); - - return fillInIntent; - } -} diff --git a/src/com/android/calendar/widget/CalendarAppWidgetService.java b/src/com/android/calendar/widget/CalendarAppWidgetService.java deleted file mode 100644 index ec702c7c..00000000 --- a/src/com/android/calendar/widget/CalendarAppWidgetService.java +++ /dev/null @@ -1,626 +0,0 @@ -/* - * 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.widget; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.CursorLoader; -import android.content.Intent; -import android.content.Loader; -import android.content.res.Resources; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.net.Uri; -import android.os.Handler; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Instances; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.View; -import android.widget.RemoteViews; -import android.widget.RemoteViewsService; - -import com.android.calendar.R; -import com.android.calendar.Utils; -import com.android.calendar.widget.CalendarAppWidgetModel.DayInfo; -import com.android.calendar.widget.CalendarAppWidgetModel.EventInfo; -import com.android.calendar.widget.CalendarAppWidgetModel.RowInfo; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicInteger; - - -public class CalendarAppWidgetService extends RemoteViewsService { - private static final String TAG = "CalendarWidget"; - - static final int EVENT_MIN_COUNT = 20; - static final int EVENT_MAX_COUNT = 100; - // Minimum delay between queries on the database for widget updates in ms - static final int WIDGET_UPDATE_THROTTLE = 500; - - private static final String EVENT_SORT_ORDER = Instances.START_DAY + " ASC, " - + Instances.START_MINUTE + " ASC, " + Instances.END_DAY + " ASC, " - + Instances.END_MINUTE + " ASC LIMIT " + EVENT_MAX_COUNT; - - private static final String EVENT_SELECTION = Calendars.VISIBLE + "=1"; - private static final String EVENT_SELECTION_HIDE_DECLINED = Calendars.VISIBLE + "=1 AND " - + Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED; - - static final String[] EVENT_PROJECTION = new String[] { - Instances.ALL_DAY, - Instances.BEGIN, - Instances.END, - Instances.TITLE, - Instances.EVENT_LOCATION, - Instances.EVENT_ID, - Instances.START_DAY, - Instances.END_DAY, - Instances.DISPLAY_COLOR, // If SDK < 16, set to Instances.CALENDAR_COLOR. - Instances.SELF_ATTENDEE_STATUS, - }; - - static final int INDEX_ALL_DAY = 0; - static final int INDEX_BEGIN = 1; - static final int INDEX_END = 2; - static final int INDEX_TITLE = 3; - static final int INDEX_EVENT_LOCATION = 4; - static final int INDEX_EVENT_ID = 5; - static final int INDEX_START_DAY = 6; - static final int INDEX_END_DAY = 7; - static final int INDEX_COLOR = 8; - static final int INDEX_SELF_ATTENDEE_STATUS = 9; - - static { - if (!Utils.isJellybeanOrLater()) { - EVENT_PROJECTION[INDEX_COLOR] = Instances.CALENDAR_COLOR; - } - } - static final int MAX_DAYS = 7; - - private static final long SEARCH_DURATION = MAX_DAYS * DateUtils.DAY_IN_MILLIS; - - /** - * Update interval used when no next-update calculated, or bad trigger time in past. - * Unit: milliseconds. - */ - private static final long UPDATE_TIME_NO_EVENTS = DateUtils.HOUR_IN_MILLIS * 6; - - @Override - public RemoteViewsFactory onGetViewFactory(Intent intent) { - return new CalendarFactory(getApplicationContext(), intent); - } - - public static class CalendarFactory extends BroadcastReceiver implements - RemoteViewsService.RemoteViewsFactory, Loader.OnLoadCompleteListener { - private static final boolean LOGD = false; - - // Suppress unnecessary logging about update time. Need to be static as this object is - // re-instanciated frequently. - // TODO: It seems loadData() is called via onCreate() four times, which should mean - // unnecessary CalendarFactory object is created and dropped. It is not efficient. - private static long sLastUpdateTime = UPDATE_TIME_NO_EVENTS; - - private Context mContext; - private Resources mResources; - private static CalendarAppWidgetModel mModel; - private static Object mLock = new Object(); - private static volatile int mSerialNum = 0; - private int mLastSerialNum = -1; - private CursorLoader mLoader; - private final Handler mHandler = new Handler(); - private static final AtomicInteger currentVersion = new AtomicInteger(0); - private final ExecutorService executor = Executors.newSingleThreadExecutor(); - private int mAppWidgetId; - private int mDeclinedColor; - private int mStandardColor; - private int mAllDayColor; - - private final Runnable mTimezoneChanged = new Runnable() { - @Override - public void run() { - if (mLoader != null) { - mLoader.forceLoad(); - } - } - }; - - private Runnable createUpdateLoaderRunnable(final String selection, - final PendingResult result, final int version) { - return new Runnable() { - @Override - public void run() { - // If there is a newer load request in the queue, skip loading. - if (mLoader != null && version >= currentVersion.get()) { - Uri uri = createLoaderUri(); - mLoader.setUri(uri); - mLoader.setSelection(selection); - synchronized (mLock) { - mLastSerialNum = ++mSerialNum; - } - mLoader.forceLoad(); - } - result.finish(); - } - }; - } - - protected CalendarFactory(Context context, Intent intent) { - mContext = context; - mResources = context.getResources(); - mAppWidgetId = intent.getIntExtra( - AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); - - mDeclinedColor = mResources.getColor(R.color.appwidget_item_declined_color); - mStandardColor = mResources.getColor(R.color.appwidget_item_standard_color); - mAllDayColor = mResources.getColor(R.color.appwidget_item_allday_color); - } - - public CalendarFactory() { - // This is being created as part of onReceive - - } - - @Override - public void onCreate() { - String selection = queryForSelection(); - initLoader(selection); - } - - @Override - public void onDataSetChanged() { - } - - @Override - public void onDestroy() { - if (mLoader != null) { - mLoader.reset(); - } - } - - @Override - public RemoteViews getLoadingView() { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_loading); - return views; - } - - @Override - public RemoteViews getViewAt(int position) { - // we use getCount here so that it doesn't return null when empty - if (position < 0 || position >= getCount()) { - return null; - } - - if (mModel == null) { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_loading); - final Intent intent = CalendarAppWidgetProvider.getLaunchFillInIntent(mContext, 0, - 0, 0, false); - views.setOnClickFillInIntent(R.id.appwidget_loading, intent); - return views; - - } - if (mModel.mEventInfos.isEmpty() || mModel.mRowInfos.isEmpty()) { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_no_events); - final Intent intent = CalendarAppWidgetProvider.getLaunchFillInIntent(mContext, 0, - 0, 0, false); - views.setOnClickFillInIntent(R.id.appwidget_no_events, intent); - return views; - } - - RowInfo rowInfo = mModel.mRowInfos.get(position); - if (rowInfo.mType == RowInfo.TYPE_DAY) { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_day); - DayInfo dayInfo = mModel.mDayInfos.get(rowInfo.mIndex); - updateTextView(views, R.id.date, View.VISIBLE, dayInfo.mDayLabel); - return views; - } else { - RemoteViews views; - final EventInfo eventInfo = mModel.mEventInfos.get(rowInfo.mIndex); - if (eventInfo.allDay) { - views = new RemoteViews(mContext.getPackageName(), - R.layout.widget_all_day_item); - } else { - views = new RemoteViews(mContext.getPackageName(), R.layout.widget_item); - } - int displayColor = Utils.getDisplayColorFromColor(eventInfo.color); - - final long now = System.currentTimeMillis(); - if (!eventInfo.allDay && eventInfo.start <= now && now <= eventInfo.end) { - views.setInt(R.id.widget_row, "setBackgroundResource", - R.drawable.agenda_item_bg_secondary); - } else { - views.setInt(R.id.widget_row, "setBackgroundResource", - R.drawable.agenda_item_bg_primary); - } - - if (!eventInfo.allDay) { - updateTextView(views, R.id.when, eventInfo.visibWhen, eventInfo.when); - updateTextView(views, R.id.where, eventInfo.visibWhere, eventInfo.where); - } - updateTextView(views, R.id.title, eventInfo.visibTitle, eventInfo.title); - - views.setViewVisibility(R.id.agenda_item_color, View.VISIBLE); - - int selfAttendeeStatus = eventInfo.selfAttendeeStatus; - if (eventInfo.allDay) { - if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_INVITED) { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_not_responded_bg); - views.setInt(R.id.title, "setTextColor", displayColor); - } else { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_responded_bg); - views.setInt(R.id.title, "setTextColor", mAllDayColor); - } - if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) { - // 40% opacity - views.setInt(R.id.agenda_item_color, "setColorFilter", - Utils.getDeclinedColorFromColor(displayColor)); - } else { - views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor); - } - } else if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) { - views.setInt(R.id.title, "setTextColor", mDeclinedColor); - views.setInt(R.id.when, "setTextColor", mDeclinedColor); - views.setInt(R.id.where, "setTextColor", mDeclinedColor); - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_responded_bg); - // 40% opacity - views.setInt(R.id.agenda_item_color, "setColorFilter", - Utils.getDeclinedColorFromColor(displayColor)); - } else { - views.setInt(R.id.title, "setTextColor", mStandardColor); - views.setInt(R.id.when, "setTextColor", mStandardColor); - views.setInt(R.id.where, "setTextColor", mStandardColor); - if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_INVITED) { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_not_responded_bg); - } else { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_responded_bg); - } - views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor); - } - - long start = eventInfo.start; - long end = eventInfo.end; - // An element in ListView. - if (eventInfo.allDay) { - String tz = Utils.getTimeZone(mContext, null); - Time recycle = new Time(); - start = Utils.convertAlldayLocalToUTC(recycle, start, tz); - end = Utils.convertAlldayLocalToUTC(recycle, end, tz); - } - final Intent fillInIntent = CalendarAppWidgetProvider.getLaunchFillInIntent( - mContext, eventInfo.id, start, end, eventInfo.allDay); - views.setOnClickFillInIntent(R.id.widget_row, fillInIntent); - return views; - } - } - - @Override - public int getViewTypeCount() { - return 5; - } - - @Override - public int getCount() { - // if there are no events, we still return 1 to represent the "no - // events" view - if (mModel == null) { - return 1; - } - return Math.max(1, mModel.mRowInfos.size()); - } - - @Override - public long getItemId(int position) { - if (mModel == null || mModel.mRowInfos.isEmpty() || position >= getCount()) { - return 0; - } - RowInfo rowInfo = mModel.mRowInfos.get(position); - if (rowInfo.mType == RowInfo.TYPE_DAY) { - return rowInfo.mIndex; - } - EventInfo eventInfo = mModel.mEventInfos.get(rowInfo.mIndex); - long prime = 31; - long result = 1; - result = prime * result + (int) (eventInfo.id ^ (eventInfo.id >>> 32)); - result = prime * result + (int) (eventInfo.start ^ (eventInfo.start >>> 32)); - return result; - } - - @Override - public boolean hasStableIds() { - return true; - } - - /** - * Query across all calendars for upcoming event instances from now - * until some time in the future. Widen the time range that we query by - * one day on each end so that we can catch all-day events. All-day - * events are stored starting at midnight in UTC but should be included - * in the list of events starting at midnight local time. This may fetch - * more events than we actually want, so we filter them out later. - * - * @param selection The selection string for the loader to filter the query with. - */ - public void initLoader(String selection) { - if (LOGD) - Log.d(TAG, "Querying for widget events..."); - - // Search for events from now until some time in the future - Uri uri = createLoaderUri(); - mLoader = new CursorLoader(mContext, uri, EVENT_PROJECTION, selection, null, - EVENT_SORT_ORDER); - mLoader.setUpdateThrottle(WIDGET_UPDATE_THROTTLE); - synchronized (mLock) { - mLastSerialNum = ++mSerialNum; - } - mLoader.registerListener(mAppWidgetId, this); - mLoader.startLoading(); - - } - - /** - * This gets the selection string for the loader. This ends up doing a query in the - * shared preferences. - */ - private String queryForSelection() { - return Utils.getHideDeclinedEvents(mContext) ? EVENT_SELECTION_HIDE_DECLINED - : EVENT_SELECTION; - } - - /** - * @return The uri for the loader - */ - private Uri createLoaderUri() { - long now = System.currentTimeMillis(); - // Add a day on either side to catch all-day events - long begin = now - DateUtils.DAY_IN_MILLIS; - long end = now + SEARCH_DURATION + DateUtils.DAY_IN_MILLIS; - - Uri uri = Uri.withAppendedPath(Instances.CONTENT_URI, Long.toString(begin) + "/" + end); - return uri; - } - - /* @VisibleForTesting */ - protected static CalendarAppWidgetModel buildAppWidgetModel( - Context context, Cursor cursor, String timeZone) { - CalendarAppWidgetModel model = new CalendarAppWidgetModel(context, timeZone); - model.buildFromCursor(cursor, timeZone); - return model; - } - - /** - * Calculates and returns the next time we should push widget updates. - */ - private long calculateUpdateTime(CalendarAppWidgetModel model, long now, String timeZone) { - // Make sure an update happens at midnight or earlier - long minUpdateTime = getNextMidnightTimeMillis(timeZone); - for (EventInfo event : model.mEventInfos) { - final long start; - final long end; - start = event.start; - end = event.end; - - // We want to update widget when we enter/exit time range of an event. - if (now < start) { - minUpdateTime = Math.min(minUpdateTime, start); - } else if (now < end) { - minUpdateTime = Math.min(minUpdateTime, end); - } - } - return minUpdateTime; - } - - private static long getNextMidnightTimeMillis(String timezone) { - Time time = new Time(); - time.setToNow(); - time.monthDay++; - time.hour = 0; - time.minute = 0; - time.second = 0; - long midnightDeviceTz = time.normalize(true); - - time.timezone = timezone; - time.setToNow(); - time.monthDay++; - time.hour = 0; - time.minute = 0; - time.second = 0; - long midnightHomeTz = time.normalize(true); - - return Math.min(midnightDeviceTz, midnightHomeTz); - } - - static void updateTextView(RemoteViews views, int id, int visibility, String string) { - views.setViewVisibility(id, visibility); - if (visibility == View.VISIBLE) { - views.setTextViewText(id, string); - } - } - - /* - * (non-Javadoc) - * @see - * android.content.Loader.OnLoadCompleteListener#onLoadComplete(android - * .content.Loader, java.lang.Object) - */ - @Override - public void onLoadComplete(Loader loader, Cursor cursor) { - if (cursor == null) { - return; - } - // If a newer update has happened since we started clean up and - // return - synchronized (mLock) { - if (cursor.isClosed()) { - Log.wtf(TAG, "Got a closed cursor from onLoadComplete"); - return; - } - - if (mLastSerialNum != mSerialNum) { - return; - } - - final long now = System.currentTimeMillis(); - String tz = Utils.getTimeZone(mContext, mTimezoneChanged); - - // Copy it to a local static cursor. - MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); - try { - mModel = buildAppWidgetModel(mContext, matrixCursor, tz); - } finally { - if (matrixCursor != null) { - matrixCursor.close(); - } - - if (cursor != null) { - cursor.close(); - } - } - - // Schedule an alarm to wake ourselves up for the next update. - // We also cancel - // all existing wake-ups because PendingIntents don't match - // against extras. - long triggerTime = calculateUpdateTime(mModel, now, tz); - - // If no next-update calculated, or bad trigger time in past, - // schedule - // update about six hours from now. - if (triggerTime < now) { - Log.w(TAG, "Encountered bad trigger time " + formatDebugTime(triggerTime, now)); - triggerTime = now + UPDATE_TIME_NO_EVENTS; - } - - final AlarmManager alertManager = (AlarmManager) mContext - .getSystemService(Context.ALARM_SERVICE); - final PendingIntent pendingUpdate = CalendarAppWidgetProvider - .getUpdateIntent(mContext); - - alertManager.cancel(pendingUpdate); - alertManager.set(AlarmManager.RTC, triggerTime, pendingUpdate); - Time time = new Time(Utils.getTimeZone(mContext, null)); - time.setToNow(); - - if (time.normalize(true) != sLastUpdateTime) { - Time time2 = new Time(Utils.getTimeZone(mContext, null)); - time2.set(sLastUpdateTime); - time2.normalize(true); - if (time.year != time2.year || time.yearDay != time2.yearDay) { - final Intent updateIntent = new Intent( - Utils.getWidgetUpdateAction(mContext)); - mContext.sendBroadcast(updateIntent); - } - - sLastUpdateTime = time.toMillis(true); - } - - AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext); - if (widgetManager == null) { - return; - } - if (mAppWidgetId == -1) { - int[] ids = widgetManager.getAppWidgetIds(CalendarAppWidgetProvider - .getComponentName(mContext)); - - widgetManager.notifyAppWidgetViewDataChanged(ids, R.id.events_list); - } else { - widgetManager.notifyAppWidgetViewDataChanged(mAppWidgetId, R.id.events_list); - } - } - } - - @Override - public void onReceive(Context context, Intent intent) { - if (LOGD) - Log.d(TAG, "AppWidgetService received an intent. It was " + intent.toString()); - mContext = context; - - // We cannot do any queries from the UI thread, so push the 'selection' query - // to a background thread. However the implementation of the latter query - // (cursor loading) uses CursorLoader which must be initiated from the UI thread, - // so there is some convoluted handshaking here. - // - // Note that as currently implemented, this must run in a single threaded executor - // or else the loads may be run out of order. - // - // TODO: Remove use of mHandler and CursorLoader, and do all the work synchronously - // in the background thread. All the handshaking going on here between the UI and - // background thread with using goAsync, mHandler, and CursorLoader is confusing. - final PendingResult result = goAsync(); - executor.submit(new Runnable() { - @Override - public void run() { - // We always complete queryForSelection() even if the load task ends up being - // canceled because of a more recent one. Optimizing this to allow - // canceling would require keeping track of all the PendingResults - // (from goAsync) to abort them. Defer this until it becomes a problem. - final String selection = queryForSelection(); - - if (mLoader == null) { - mAppWidgetId = -1; - mHandler.post(new Runnable() { - @Override - public void run() { - initLoader(selection); - result.finish(); - } - }); - } else { - mHandler.post(createUpdateLoaderRunnable(selection, result, - currentVersion.incrementAndGet())); - } - } - }); - } - } - - /** - * Format given time for debugging output. - * - * @param unixTime Target time to report. - * @param now Current system time from {@link System#currentTimeMillis()} - * for calculating time difference. - */ - static String formatDebugTime(long unixTime, long now) { - Time time = new Time(); - time.set(unixTime); - - long delta = unixTime - now; - if (delta > DateUtils.MINUTE_IN_MILLIS) { - delta /= DateUtils.MINUTE_IN_MILLIS; - return String.format("[%d] %s (%+d mins)", unixTime, - time.format("%H:%M:%S"), delta); - } else { - delta /= DateUtils.SECOND_IN_MILLIS; - return String.format("[%d] %s (%+d secs)", unixTime, - time.format("%H:%M:%S"), delta); - } - } -} -- cgit v1.2.3 From 6188fe370a3369c619cf96c4ab021e7a3068a77e Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Tue, 13 Jul 2021 21:52:13 +0000 Subject: AOSP/Calendar - Android.bp changes The "CalendarJava" android app property has been deleted along with the "exclude_srcs" property since the .java files in the source code directory have been deleted. Test: manual - "make Calendar" build successfully. Change-Id: I861d131fcce9f43a0e8993be50eb0f2bd2a422a0 --- Android.bp | 88 -------------------------------------------------------------- 1 file changed, 88 deletions(-) diff --git a/Android.bp b/Android.bp index 2fb4ad1c..d732f94f 100644 --- a/Android.bp +++ b/Android.bp @@ -16,54 +16,6 @@ license { } // Include res dir from chips -exclude_srcsd = [ - "src/**/calendar/alerts/AlarmManagerInterface.java", - "src/**/calendar/alerts/AlarmScheduler.java", - "src/**/calendar/alerts/AlertReceiver.java", - "src/**/calendar/alerts/AlertService.java", - "src/**/calendar/alerts/AlertUtils.java", - "src/**/calendar/alerts/DismissAlarmsService.java", - "src/**/calendar/alerts/GlobalDismissManager.java", - "src/**/calendar/alerts/InitAlarmsService.java", - "src/**/calendar/alerts/NotificationMgr.java", - "src/**/calendar/alerts/QuickResponseActivity.java", - "src/**/calendar/month/MonthByWeekAdapter.java", - "src/**/calendar/month/MonthByWeekFragment.java", - "src/**/calendar/month/SimpleWeeksAdapter.java", - "src/**/calendar/widget/CalendarAppWidgetModel.java", - "src/**/calendar/widget/CalendarAppWidgetProvider.java", - "src/**/calendar/widget/CalendarAppWidgetService.java", - "src/**/calendar/AllInOneActivity.java", - "src/**/calendar/CalendarController.java", - "src/**/calendar/DayOfMonthDrawable.java", - "src/**/calendar/DayView.java", - "src/**/calendar/Event.java", - "src/**/calendar/EventInfoActivity.java", - "src/**/calendar/EventInfoFragment.java", - "src/**/calendar/StickyHeaderListView.java", - "src/**/calendar/Utils.java", -] - -exclude_srcsm = [ - "src/**/calendar/month/MonthListView.java", - "src/**/calendar/month/MonthWeekEventsView.java", - "src/**/calendar/month/SimpleWeekView.java", - "src/**/calendar/month/SimpleDayPickerFragment.java", - "src/**/calendar/AsyncQueryServiceHelper.java", - "src/**/calendar/CalendarApplication.java", - "src/**/calendar/CalendarBackupAgent.java", - "src/**/calendar/CalendarData.java", - "src/**/calendar/CalendarUtils.java", - "src/**/calendar/CalendarViewAdapter.java", - "src/**/calendar/DayFragment.java", - "src/**/calendar/EventGeometry.java", - "src/**/calendar/EventLoader.java", - "src/**/calendar/GeneralPreferences.java", - "src/**/calendar/GoogleCalendarUriIntentFilter.java", - "src/**/calendar/MultiStateButton.java", - "src/**/calendar/OtherPreferences.java", - "src/**/calendar/UpgradeReceiver.java", -] android_app { name: "Calendar", @@ -73,49 +25,9 @@ android_app { }, srcs: [ - "src/**/*.java", "src/**/*.kt", ], - exclude_srcs: exclude_srcsd + exclude_srcsm, - - // bundled - //LOCAL_STATIC_JAVA_LIBRARIES += - //# android-common - //# libchips - //# calendar-common - - // unbundled - static_libs: [ - "android-common", - "libchips", - "colorpicker", - "android-opt-timezonepicker", - "androidx.legacy_legacy-support-v4", - "calendar-common", - ], - - sdk_version: "current", - optimize: { - proguard_flags_files: ["proguard.flags"], - }, - - product_specific: true, - - aaptflags: ["--auto-add-overlay"], -} - -android_app { - name: "CalendarJava", - - jacoco: { - include_filter: ["com.android.calendar.**"], - }, - - srcs: [ - "src/**/*.java", - ], - // bundled //LOCAL_STATIC_JAVA_LIBRARIES += //# android-common -- cgit v1.2.3 From 1f3d22701487e886fc4934953f8d90c5dae80c6d Mon Sep 17 00:00:00 2001 From: Damian Patel Date: Thu, 15 Jul 2021 16:09:47 +0000 Subject: API level 30 upgrade: AOSP/Calendar The AndroidManifest.xml file of the AOSP Calendar app has been upgrade so that its targetSDKversion is 30. Test: manual - after the change, the Calendar app built, initialized, and ran successfully with all of the UI features functioning properly. Unit tests for calendar were also run and the output can be seen below: com.android.calendar.FormatDateRangeTest:. com.android.calendar.UtilsTests:.................. com.android.calendar.WeekNumberTest:. com.android.calendar.widget.CalendarAppWidgetServiceTest:.. Test results for InstrumentationTestRunner=...................... Time: 0.203 OK (22 tests) General CTS tests were run as well and the output is below: ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 14m 0s Total aggregated tests run time: 14m 0s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.45 tests/sec [376 tests / 840170 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 8439 ms || clean = 2153 ms Total preparation time: 8s || Total tear down time: 2s ======================================================= =============== Summary =============== Total Run time: 15m 38s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== Change-Id: If7c8b12fb37b126ebc0d00df8f0d65730e631c20 --- AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c9c5a04b..686be7a6 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -38,7 +38,7 @@ - + Date: Thu, 16 Sep 2021 17:22:53 -0700 Subject: AOSP/Calendar - Fix closing of system dialogs broadcast without permission. Deleted closeNotificationShade method thus not invoking ACTION_CLOSE_SYSTEM_DIALOGS intent. Making same changes as java from https://googleplex-android-review.git.corp.google.com/c/platform/packages/apps/Calendar/+/15845189 Test: are done in ag/15845189 and on aosp-master Bug: 190403923 Change-Id: I39bc668a5671cce94372063246c13bc09dd569fa --- src/com/android/calendar/alerts/AlertReceiver.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/com/android/calendar/alerts/AlertReceiver.kt b/src/com/android/calendar/alerts/AlertReceiver.kt index 31750065..21afa90c 100644 --- a/src/com/android/calendar/alerts/AlertReceiver.kt +++ b/src/com/android/calendar/alerts/AlertReceiver.kt @@ -45,12 +45,6 @@ class AlertReceiver : BroadcastReceiver() { if (AlertService.DEBUG) { Log.d(TAG, "onReceive: a=" + intent.getAction().toString() + " " + intent.toString()) } - closeNotificationShade(context) - } - - private fun closeNotificationShade(context: Context) { - val closeNotificationShadeIntent = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) - context.sendBroadcast(closeNotificationShadeIntent) } companion object { -- cgit v1.2.3 From d6ca0d664eff944632efd979bf1ce8c0cc06adf2 Mon Sep 17 00:00:00 2001 From: Raman Tenneti Date: Tue, 14 Sep 2021 17:22:43 -0700 Subject: [DO NOT MERGE] AOSP/Calendar - Fix closing of system dialogs broadcast without permission. Deleted closeNotificationShade method thus not invoking ACTION_CLOSE_SYSTEM_DIALOGS intent. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/product/app/Calendar/Calendar.apk Success $ adb install -r -t -d out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases The following CTS test results are same as before the change: ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 15m 17s Total aggregated tests run time: 15m 17s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.41 tests/sec [376 tests / 917885 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 4124 ms || clean = 1569 ms Total preparation time: 4s || Total tear down time: 1s ======================================================= =============== Summary =============== Total Run time: 16m 48s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== .../bug_190403923$ adb install -r -t -d ./poc.apk Performing Streamed Install Success .../bug_190403923$ adb shell am start -n com.example.poc/.MainActivity Starting: Intent { cmp=com.example.poc/.MainActivity } .../bug_190403923$ adb logcat -d | grep AutofillManagerService Bug: 190403923 Change-Id: I47f3c5845d960c1f7515282af4b757b0b9e9ae8e --- src/com/android/calendar/alerts/AlertReceiver.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/com/android/calendar/alerts/AlertReceiver.java b/src/com/android/calendar/alerts/AlertReceiver.java index ce80cae1..ee0c508d 100644 --- a/src/com/android/calendar/alerts/AlertReceiver.java +++ b/src/com/android/calendar/alerts/AlertReceiver.java @@ -80,7 +80,6 @@ public class AlertReceiver extends BroadcastReceiver { if (AlertService.DEBUG) { Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); } - closeNotificationShade(context); } public static NotificationWrapper makeBasicNotification(Context context, String title, @@ -115,9 +114,4 @@ public class AlertReceiver extends BroadcastReceiver { } return notificationBuilder.getNotification(); } - - private void closeNotificationShade(Context context) { - Intent closeNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - context.sendBroadcast(closeNotificationShadeIntent); - } } -- cgit v1.2.3 From ae487ada1969c32e7bbd32d8a83fc587506cd51c Mon Sep 17 00:00:00 2001 From: Raman Tenneti Date: Tue, 14 Sep 2021 17:22:43 -0700 Subject: [DO NOT MERGE] AOSP/Calendar - Fix closing of system dialogs broadcast without permission. Deleted closeNotificationShade method thus not invoking ACTION_CLOSE_SYSTEM_DIALOGS intent. Test: manual - build tests, unit tests, and CTS tests were conducted using the following commands: $ make Calendar $ make CalendarTests -j $ adb install -r -t -d out/target/product/bonito/product/app/Calendar/Calendar.apk Success $ adb install -r -t -d out/target/product/bonito/testcases/CalendarTests/arm64/CalendarTests.apk Success $ adb shell am instrument -w com.android.calendar.tests General CTS Tests: $ tools/cts-tradefed cts-tf > run cts -m CtsProviderTestCases The following CTS test results are same as before the change: ================= Results ================== =============== Consumed Time ============== arm64-v8a CtsProviderTestCases: 15m 17s Total aggregated tests run time: 15m 17s ============== TOP 1 Slow Modules ============== arm64-v8a CtsProviderTestCases: 0.41 tests/sec [376 tests / 917885 msec] ============== Modules Preparation Times ============== arm64-v8a CtsProviderTestCases => prep = 4124 ms || clean = 1569 ms Total preparation time: 4s || Total tear down time: 1s ======================================================= =============== Summary =============== Total Run time: 16m 48s 1/1 modules completed Total Tests : 376 PASSED : 362 FAILED : 0 IGNORED : 6 ASSUMPTION_FAILURE: 8 ============== End of Results ============== .../bug_190403923$ adb install -r -t -d ./poc.apk Performing Streamed Install Success .../bug_190403923$ adb shell am start -n com.example.poc/.MainActivity Starting: Intent { cmp=com.example.poc/.MainActivity } .../bug_190403923$ adb logcat -d | grep AutofillManagerService Bug: 190403923 Change-Id: I47f3c5845d960c1f7515282af4b757b0b9e9ae8e --- src/com/android/calendar/alerts/AlertReceiver.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/com/android/calendar/alerts/AlertReceiver.java b/src/com/android/calendar/alerts/AlertReceiver.java index ce80cae1..ee0c508d 100644 --- a/src/com/android/calendar/alerts/AlertReceiver.java +++ b/src/com/android/calendar/alerts/AlertReceiver.java @@ -80,7 +80,6 @@ public class AlertReceiver extends BroadcastReceiver { if (AlertService.DEBUG) { Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); } - closeNotificationShade(context); } public static NotificationWrapper makeBasicNotification(Context context, String title, @@ -115,9 +114,4 @@ public class AlertReceiver extends BroadcastReceiver { } return notificationBuilder.getNotification(); } - - private void closeNotificationShade(Context context) { - Intent closeNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - context.sendBroadcast(closeNotificationShadeIntent); - } } -- cgit v1.2.3 From 934500069bab6f78683cfe5a3444374b5679618f Mon Sep 17 00:00:00 2001 From: Raman Tenneti Date: Thu, 16 Sep 2021 17:22:53 -0700 Subject: AOSP/Calendar - Fix closing of system dialogs broadcast without permission. Deleted closeNotificationShade method thus not invoking ACTION_CLOSE_SYSTEM_DIALOGS intent. Making same changes as java from https://googleplex-android-review.git.corp.google.com/c/platform/packages/apps/Calendar/+/15845189 Test: are done in ag/15845189 and on aosp-master Bug: 190403923 Bug: 201673759 Change-Id: I39bc668a5671cce94372063246c13bc09dd569fa (cherry picked from commit 21ea50b26386b3a68db5fef6f8d2519db596debf) --- src/com/android/calendar/alerts/AlertReceiver.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/com/android/calendar/alerts/AlertReceiver.kt b/src/com/android/calendar/alerts/AlertReceiver.kt index 31750065..21afa90c 100644 --- a/src/com/android/calendar/alerts/AlertReceiver.kt +++ b/src/com/android/calendar/alerts/AlertReceiver.kt @@ -45,12 +45,6 @@ class AlertReceiver : BroadcastReceiver() { if (AlertService.DEBUG) { Log.d(TAG, "onReceive: a=" + intent.getAction().toString() + " " + intent.toString()) } - closeNotificationShade(context) - } - - private fun closeNotificationShade(context: Context) { - val closeNotificationShadeIntent = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) - context.sendBroadcast(closeNotificationShadeIntent) } companion object { -- cgit v1.2.3 From af7a8e4ffa47217e6ec69f10a48fc9b0db21dc4b Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Mon, 6 Dec 2021 18:23:45 +0000 Subject: Calendar: post notifications permission so the package can send notifications on T+ Bug: 194833441 Change-Id: I2d412d8949ce66e17b7f7110bbe6101aabaa3a41 --- AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 686be7a6..fed61b0c 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -38,6 +38,7 @@ + -- cgit v1.2.3 From 6d2455d18a2d5f17c3de68e9977514a506f43f7f Mon Sep 17 00:00:00 2001 From: Raman Tenneti Date: Mon, 20 Dec 2021 11:48:22 -0800 Subject: Deskclock - set target sdk version to 30. Made changes to Android.bp to match versions in AndroidManifest.xml Bug: 209469462 Test: manual Change-Id: I2ed5d940d52e080e553c001890bab7ccd6efc73e --- Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.bp b/Android.bp index d732f94f..3c6477b3 100644 --- a/Android.bp +++ b/Android.bp @@ -45,6 +45,7 @@ android_app { ], sdk_version: "current", + target_sdk_version: "30", optimize: { proguard_flags_files: ["proguard.flags"], }, -- cgit v1.2.3 From fad091f839fe143a5f81c4c8e43bbaf7d2c2a9c5 Mon Sep 17 00:00:00 2001 From: Yurii Zubrytskyi Date: Mon, 18 Apr 2022 17:55:16 -0700 Subject: packages/apps/Calendar - Tiramisu is now 33 Fix the overridden interfaces for the new SDK Bug: 225745567 Test: Build Change-Id: I320e8846d90e44b22ab5fe2a4afb69e711e38ba9 Merged-In: I320e8846d90e44b22ab5fe2a4afb69e711e38ba9 --- src/com/android/calendar/AllInOneActivity.kt | 8 ++++---- src/com/android/calendar/DayView.kt | 16 ++++++++-------- src/com/android/calendar/EventInfoFragment.kt | 6 +++--- src/com/android/calendar/month/MonthByWeekAdapter.kt | 4 ++-- src/com/android/calendar/month/SimpleWeeksAdapter.kt | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/com/android/calendar/AllInOneActivity.kt b/src/com/android/calendar/AllInOneActivity.kt index 1747bf58..6c2e825f 100644 --- a/src/com/android/calendar/AllInOneActivity.kt +++ b/src/com/android/calendar/AllInOneActivity.kt @@ -122,11 +122,11 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe private var mVerticalControlsParams: LinearLayout.LayoutParams? = null private val mSlideAnimationDoneListener: AnimatorListener = object : AnimatorListener { @Override - override fun onAnimationCancel(animation: Animator?) { + override fun onAnimationCancel(animation: Animator) { } @Override - override fun onAnimationEnd(animation: Animator?) { + override fun onAnimationEnd(animation: Animator) { val visibility: Int = if (mShowSideViews) View.VISIBLE else View.GONE mMiniMonth?.setVisibility(visibility) mCalendarsList?.setVisibility(visibility) @@ -134,11 +134,11 @@ class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListe } @Override - override fun onAnimationRepeat(animation: Animator?) { + override fun onAnimationRepeat(animation: Animator) { } @Override - override fun onAnimationStart(animation: Animator?) { + override fun onAnimationStart(animation: Animator) { } } diff --git a/src/com/android/calendar/DayView.kt b/src/com/android/calendar/DayView.kt index 58126f20..42621638 100644 --- a/src/com/android/calendar/DayView.kt +++ b/src/com/android/calendar/DayView.kt @@ -211,17 +211,17 @@ class DayView( var mAnimatorListener: AnimatorListenerAdapter = object : AnimatorListenerAdapter() { @Override - override fun onAnimationStart(animation: Animator?) { + override fun onAnimationStart(animation: Animator) { mScrolling = true } @Override - override fun onAnimationCancel(animation: Animator?) { + override fun onAnimationCancel(animation: Animator) { mScrolling = false } @Override - override fun onAnimationEnd(animation: Animator?) { + override fun onAnimationEnd(animation: Animator) { mScrolling = false resetSelectedHour() invalidate() @@ -1154,7 +1154,7 @@ class DayView( private val mStart: Time private val mEnd: Time @Override - override fun onAnimationEnd(animation: Animation?) { + override fun onAnimationEnd(animation: Animation) { var view = mViewSwitcher.getCurrentView() as DayView view.mViewStartX = 0 view = mViewSwitcher.getNextView() as DayView @@ -1168,11 +1168,11 @@ class DayView( } @Override - override fun onAnimationRepeat(animation: Animation?) { + override fun onAnimationRepeat(animation: Animation) { } @Override - override fun onAnimationStart(animation: Animation?) { + override fun onAnimationStart(animation: Animation) { } init { @@ -2682,7 +2682,7 @@ class DayView( animator.setDuration(ANIMATION_DURATION) animator.addListener(object : AnimatorListenerAdapter() { @Override - override fun onAnimationEnd(animation: Animator?) { + override fun onAnimationEnd(animation: Animator) { if (!mCancellingAnimations) { // when finished, set this to 0 to signify not animating mAnimateDayHeight = 0 @@ -3038,7 +3038,7 @@ class DayView( } // ScaleGestureDetector.OnScaleGestureListener - override fun onScaleEnd(detector: ScaleGestureDetector?) { + override fun onScaleEnd(detector: ScaleGestureDetector) { mScrollStartY = mViewStartY mInitialScrollY = 0f mInitialScrollX = 0f diff --git a/src/com/android/calendar/EventInfoFragment.kt b/src/com/android/calendar/EventInfoFragment.kt index 2f007ab5..fcc27fc8 100644 --- a/src/com/android/calendar/EventInfoFragment.kt +++ b/src/com/android/calendar/EventInfoFragment.kt @@ -424,7 +424,7 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr mAnimateAlpha?.addListener(object : AnimatorListenerAdapter() { var defLayerType = 0 @Override - override fun onAnimationStart(animation: Animator?) { + override fun onAnimationStart(animation: Animator) { // Use hardware layer for better performance during animation defLayerType = mScrollView?.getLayerType() as Int mScrollView?.setLayerType(View.LAYER_TYPE_HARDWARE, null) @@ -435,12 +435,12 @@ class EventInfoFragment : DialogFragment, OnCheckedChangeListener, CalendarContr } @Override - override fun onAnimationCancel(animation: Animator?) { + override fun onAnimationCancel(animation: Animator) { mScrollView?.setLayerType(defLayerType, null) } @Override - override fun onAnimationEnd(animation: Animator?) { + override fun onAnimationEnd(animation: Animator) { mScrollView?.setLayerType(defLayerType, null) // Do not cross fade after the first time mNoCrossFade = true diff --git a/src/com/android/calendar/month/MonthByWeekAdapter.kt b/src/com/android/calendar/month/MonthByWeekAdapter.kt index da617950..c67b3562 100644 --- a/src/com/android/calendar/month/MonthByWeekAdapter.kt +++ b/src/com/android/calendar/month/MonthByWeekAdapter.kt @@ -308,12 +308,12 @@ class MonthByWeekAdapter(context: Context?, params: HashMap) : */ protected inner class CalendarGestureListener : GestureDetector.SimpleOnGestureListener() { @Override - override fun onSingleTapUp(e: MotionEvent?): Boolean { + override fun onSingleTapUp(e: MotionEvent): Boolean { return true } @Override - override fun onLongPress(e: MotionEvent?) { + override fun onLongPress(e: MotionEvent) { if (mLongClickedView != null) { val day: Time? = mLongClickedView?.getDayFromLocation(mClickedXLocation) if (day != null) { diff --git a/src/com/android/calendar/month/SimpleWeeksAdapter.kt b/src/com/android/calendar/month/SimpleWeeksAdapter.kt index 67be4d89..164f05c5 100644 --- a/src/com/android/calendar/month/SimpleWeeksAdapter.kt +++ b/src/com/android/calendar/month/SimpleWeeksAdapter.kt @@ -243,7 +243,7 @@ open class SimpleWeeksAdapter(context: Context, params: HashMap?) */ protected inner class CalendarGestureListener : GestureDetector.SimpleOnGestureListener() { @Override - override fun onSingleTapUp(e: MotionEvent?): Boolean { + override fun onSingleTapUp(e: MotionEvent): Boolean { return true } } -- cgit v1.2.3