diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2021-06-29 01:10:35 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-06-29 01:10:35 +0000 |
commit | b93cd7f52ce8e82ea29be680fb66d69249965960 (patch) | |
tree | 47acf54065668331efc5ddfa6bd8461eddad4dd0 | |
parent | 3f6902af6ffecbf0a9251a7866b3211d0d12caf6 (diff) | |
parent | 2882e5f0c09de852ef9ac6a5234b45ecbf622687 (diff) | |
download | Calendar-b93cd7f52ce8e82ea29be680fb66d69249965960.tar.gz |
Merge changes from topic "MonthByWeekFragment"
* changes:
AOSP/Calendar - MonthByWeekFragment fully converted with bp file
AOSP/Calendar - Inital conversion of MonthByWeekFragment
AOSP/Calendar - Copy of MonthByWeekFragment.java
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | src/com/android/calendar/month/MonthByWeekFragment.kt | 495 |
2 files changed, 496 insertions, 0 deletions
@@ -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 new file mode 100644 index 00000000..d03b7464 --- /dev/null +++ b/src/com/android/calendar/month/MonthByWeekFragment.kt @@ -0,0 +1,495 @@ +/* + * 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 + * + * 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.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(), + protected var mIsMiniMonth: Boolean = true +) : SimpleDayPickerFragment(initialTime), CalendarController.EventHandler, + LoaderManager.LoaderCallbacks<Cursor?>, 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 + override 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() + } + } + } + private val mUpdateLoader: Runnable = object : Runnable { + @Override + override fun 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 + var mLoadingRunnable: Runnable = object : Runnable { + @Override + override fun run() { + if (!mIsDetached) { + mLoader = getLoaderManager().initLoader( + 0, null, + this@MonthByWeekFragment + ) as CursorLoader + } + } + } + + /** + * Updates the uri used by the loader according to the current position of + * the listview. + * + * @return The new Uri to use + */ + private fun updateUri(): Uri { + val child: SimpleWeekView? = mListView.getChildAt(0) as? SimpleWeekView + if (child != null) { + val julianDay: Int = child?.getFirstJulianDay() + mFirstLoadedJulianDay = julianDay + } + // -1 to ensure we get all day events from any time zone + 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) + val end: Long = mTempTime.toMillis(true) + + // Create a new uri with the updated times + 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 fun updateLoadedDays() { + val pathSegments = mEventUri?.getPathSegments() + val size: Int = pathSegments?.size as Int + if (size <= 2) { + return + } + 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) + mLastLoadedJulianDay = Time.getJulianDay(last, mTempTime.gmtoff) + } + + protected fun updateWhere(): String { + // 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) + } + return where + } + + private fun 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 + override fun onAttach(activity: Activity) { + super.onAttach(activity) + mTZUpdater.run() + if (mAdapter != null) { + mAdapter.setSelectedDay(mSelectedDay) + } + mIsDetached = false + val viewConfig: ViewConfiguration = ViewConfiguration.get(activity) + 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 + // calendar controls. + if (mShowCalendarControls) { + mEventsLoadingDelay = res.getInteger(R.integer.calendar_controls_animation_time) + } + mShowDetailsInMonth = res.getBoolean(R.bool.show_details_in_month) + } + + @Override + override fun onDetach() { + mIsDetached = true + super.onDetach() + if (mShowCalendarControls) { + if (mListView != null) { + mListView.removeCallbacks(mLoadingRunnable) + } + } + } + + @Override + protected override fun setUpAdapter() { + mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext) + mShowWeekNumber = Utils.getShowWeekNumber(mContext) + val weekParams: HashMap<String, Int> = HashMap<String, Int>() + 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.registerDataSetObserver(mObserver) + } else { + mAdapter.updateParams(weekParams) + } + mAdapter.notifyDataSetChanged() + } + + @Override + override 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 { + inflater.inflate(R.layout.full_month_by_week, container, false) + } + 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) + 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.toLong()) + } else { + mLoader = getLoaderManager().initLoader(0, null, this) as CursorLoader + } + mAdapter.setListView(mListView) + } + + @Override + protected override fun setUpHeader() { + if (mIsMiniMonth) { + super.setUpHeader() + return + } + mDayLabels = arrayOfNulls<String>(7) + for (i in Calendar.SUNDAY..Calendar.SATURDAY) { + mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString( + i, + DateUtils.LENGTH_MEDIUM + ).toUpperCase() + } + } + + // TODO + @Override + override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor?>? { + if (mIsMiniMonth) { + return null + } + var loader: CursorLoader? + synchronized(mUpdateLoader) { + mFirstLoadedJulianDay = + (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.toLong()) + } + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Returning new loader with uri: $mEventUri") + } + return loader + } + + @Override + 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()) + } + mDaysPerWeek = Utils.getDaysPerWeek(mContext) + updateHeader() + mAdapter.setSelectedDay(mSelectedDay) + mTZUpdater.run() + mTodayUpdater.run() + goTo(mSelectedDay.toMillis(true), false, true, false) + } + + @Override + override fun onLoadFinished(loader: Loader<Cursor?>?, data: Cursor?) { + synchronized(mUpdateLoader) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d( + TAG, + "Found " + data?.getCount()?.toString() + " cursor entries for uri " + mEventUri + ) + } + val cLoader: CursorLoader = loader as CursorLoader + 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 + } + val events: ArrayList<Event?>? = ArrayList<Event?>() + Event.buildEventsFromCursor( + events, data, mContext, mFirstLoadedJulianDay, mLastLoadedJulianDay + ) + (mAdapter as MonthByWeekAdapter).setEvents( + mFirstLoadedJulianDay, + mLastLoadedJulianDay - mFirstLoadedJulianDay + 1, events + ) + } + } + + @Override + override fun onLoaderReset(loader: Loader<Cursor?>?) { + } + + @Override + override fun eventsChanged() { + // TODO remove this after b/3387924 is resolved + if (mLoader != null) { + mLoader?.forceLoad() + } + } + + @get:Override override val supportedEventTypes: Long + get() = EventType.GO_TO or EventType.EVENTS_CHANGED + + @Override + 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) 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 + val delayAnimation: Boolean = + 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() + } + }, if (delayAnimation) GOTO_SCROLL_DURATION.toLong() else 0L) + } + } else if (event?.eventType == EventType.EVENTS_CHANGED) { + eventsChanged() + } + } + + @Override + 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) { + mSelectedDay.set(mDesiredDay) + mAdapter.setSelectedDay(mDesiredDay) + useSelected = true + } else { + mSelectedDay.set(time) + mAdapter.setSelectedDay(time) + } + 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) { + val offset: Long = + 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 + DateUtils.FORMAT_NO_MONTH_DAY.toLong() or + DateUtils.FORMAT_SHOW_YEAR.toLong(), null, null + ) + } + } + + @Override + override fun onScrollStateChanged(view: AbsListView?, scrollState: Int) { + synchronized(mUpdateLoader) { + if (scrollState != OnScrollListener.SCROLL_STATE_IDLE) { + mShouldLoad = false + stopLoader() + mDesiredDay.setToNow() + } else { + mHandler.removeCallbacks(mUpdateLoader) + mShouldLoad = true + mHandler.postDelayed(mUpdateLoader, LOADER_DELAY.toLong()) + } + } + if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { + mUserScrolled = true + } + mScrollStateChangedRunnable.doScrollStateChange(view, scrollState) + } + + @Override + override 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 |