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
diff --git a/src/com/android/calendar/widget/CalendarAppWidgetService.kt b/src/com/android/calendar/widget/CalendarAppWidgetService.kt
index ec702c7..1a72eee 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
-package com.android.calendar.widget;
+import android.app.AlarmManager
-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;
+class CalendarAppWidgetService : RemoteViewsService() {
+ companion object {
+ private const val TAG = "CalendarWidget"
+ const val EVENT_MIN_COUNT = 20
+ const val EVENT_MAX_COUNT = 100
-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;
+ // 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<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
+ )
+ 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
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicInteger;
+ /**
+ * 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
+ /**
+ * 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
+ )
+ }
+ }
-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;
+ init {
+ 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);
+ fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
+ return CalendarFactory(getApplicationContext(), intent)
}
- public static class CalendarFactory extends BroadcastReceiver implements
- RemoteViewsService.RemoteViewsFactory, Loader.OnLoadCompleteListener<Cursor> {
- 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<Cursor?> {
+ 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
}
}
+ @get:Override val viewTypeCount: Int
+ get() = 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
- public int getViewTypeCount() {
- return 5;
+ fun getItemId(position: Int): Long {
+ if (mModel == null || mModel.mRowInfos.isEmpty() || position >= count) {
+ return 0
+ }
+ val rowInfo: RowInfo = mModel.mRowInfos.get(position)
+ if (rowInfo.mType === RowInfo.TYPE_DAY) {
+ return rowInfo.mIndex
+ }
+ 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 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;
+ fun hasStableIds(): Boolean {
+ return true
}
/**
@@ -368,100 +370,68 @@
*
* @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 @@
* .content.Loader, java.lang.Object)
*/
@Override
- public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
+ fun onLoadComplete(loader: Loader<Cursor?>?, 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 @@
// 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 @@
// 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()
+ )
+ )
}
}
- });
+ })
+ }
+
+ 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)
+ }
+ }
}
}
-
- /**
- * 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);
- }
- }
-}
+}
\ No newline at end of file