1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.calendar 17 18 import com.android.calendar.CalendarController.EventInfo 19 import com.android.calendar.CalendarController.EventType 20 import android.app.Fragment 21 import android.content.Context 22 import android.os.Bundle 23 import android.text.format.Time 24 import android.view.LayoutInflater 25 import android.view.View 26 import android.view.ViewGroup 27 import android.widget.FrameLayout.LayoutParams 28 import android.view.animation.Animation 29 import android.view.animation.AnimationUtils 30 import android.widget.ProgressBar 31 import android.widget.ViewSwitcher 32 import android.widget.ViewSwitcher.ViewFactory 33 34 /** 35 * This is the base class for Day and Week Activities. 36 */ 37 class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory { 38 protected var mProgressBar: ProgressBar? = null 39 protected var mViewSwitcher: ViewSwitcher? = null 40 protected var mInAnimationForward: Animation? = null 41 protected var mOutAnimationForward: Animation? = null 42 protected var mInAnimationBackward: Animation? = null 43 protected var mOutAnimationBackward: Animation? = null 44 var mEventLoader: EventLoader? = null 45 var mSelectedDay: Time = Time() 46 private val mTZUpdater: Runnable = object : Runnable { runnull47 override fun run() { 48 if (!this@DayFragment.isAdded()) { 49 return 50 } 51 val tz: String? = Utils.getTimeZone(getActivity(), this) 52 mSelectedDay.timezone = tz 53 mSelectedDay.normalize(true) 54 } 55 } 56 private var mNumDays = 0 57 58 constructor() { 59 mSelectedDay.setToNow() 60 } 61 62 constructor(timeMillis: Long, numOfDays: Int) { 63 mNumDays = numOfDays 64 if (timeMillis == 0L) { 65 mSelectedDay.setToNow() 66 } else { 67 mSelectedDay.set(timeMillis) 68 } 69 } 70 onCreatenull71 override fun onCreate(icicle: Bundle?) { 72 super.onCreate(icicle) 73 val context: Context = getActivity() 74 mInAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_in) 75 mOutAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_out) 76 mInAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_in) 77 mOutAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_out) 78 mEventLoader = EventLoader(context) 79 } 80 onCreateViewnull81 override fun onCreateView( 82 inflater: LayoutInflater?, 83 container: ViewGroup?, 84 savedInstanceState: Bundle? 85 ): View? { 86 val v: View? = inflater?.inflate(R.layout.day_activity, null) 87 mViewSwitcher = v?.findViewById(R.id.switcher) as? ViewSwitcher 88 mViewSwitcher?.setFactory(this) 89 mViewSwitcher?.getCurrentView()?.requestFocus() 90 (mViewSwitcher?.getCurrentView() as? DayView)?.updateTitle() 91 return v 92 } 93 makeViewnull94 override fun makeView(): View { 95 mTZUpdater.run() 96 val view = DayView(getActivity(), CalendarController 97 .getInstance(getActivity()), mViewSwitcher, mEventLoader, mNumDays) 98 view.setId(DayFragment.Companion.VIEW_ID) 99 view.setLayoutParams(LayoutParams( 100 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)) 101 view.setSelected(mSelectedDay, false, false) 102 return view 103 } 104 onResumenull105 override fun onResume() { 106 super.onResume() 107 mEventLoader!!.startBackgroundThread() 108 mTZUpdater.run() 109 eventsChanged() 110 var view: DayView? = mViewSwitcher?.getCurrentView() as? DayView 111 view?.handleOnResume() 112 view?.restartCurrentTimeUpdates() 113 view = mViewSwitcher?.getNextView() as? DayView 114 view?.handleOnResume() 115 view?.restartCurrentTimeUpdates() 116 } 117 onSaveInstanceStatenull118 override fun onSaveInstanceState(outState: Bundle?) { 119 super.onSaveInstanceState(outState) 120 } 121 onPausenull122 override fun onPause() { 123 super.onPause() 124 var view: DayView? = mViewSwitcher?.getCurrentView() as? DayView 125 view?.cleanup() 126 view = mViewSwitcher?.getNextView() as? DayView 127 view?.cleanup() 128 mEventLoader!!.stopBackgroundThread() 129 130 // Stop events cross-fade animation 131 view?.stopEventsAnimation() 132 (mViewSwitcher?.getNextView() as? DayView)?.stopEventsAnimation() 133 } 134 startProgressSpinnernull135 fun startProgressSpinner() { 136 // start the progress spinner 137 mProgressBar?.setVisibility(View.VISIBLE) 138 } 139 stopProgressSpinnernull140 fun stopProgressSpinner() { 141 // stop the progress spinner 142 mProgressBar?.setVisibility(View.GONE) 143 } 144 goTonull145 private fun goTo(goToTime: Time?, ignoreTime: Boolean, animateToday: Boolean) { 146 if (mViewSwitcher == null) { 147 // The view hasn't been set yet. Just save the time and use it later. 148 mSelectedDay.set(goToTime) 149 return 150 } 151 val currentView: DayView? = mViewSwitcher?.getCurrentView() as? DayView 152 153 // How does goTo time compared to what's already displaying? 154 val diff: Int = currentView?.compareToVisibleTimeRange(goToTime as Time) as Int 155 if (diff == 0) { 156 // In visible range. No need to switch view 157 currentView.setSelected(goToTime, ignoreTime, animateToday) 158 } else { 159 // Figure out which way to animate 160 if (diff > 0) { 161 mViewSwitcher?.setInAnimation(mInAnimationForward) 162 mViewSwitcher?.setOutAnimation(mOutAnimationForward) 163 } else { 164 mViewSwitcher?.setInAnimation(mInAnimationBackward) 165 mViewSwitcher?.setOutAnimation(mOutAnimationBackward) 166 } 167 val next: DayView? = mViewSwitcher?.getNextView() as? DayView 168 if (ignoreTime) { 169 next!!.firstVisibleHour = currentView.firstVisibleHour 170 } 171 next?.setSelected(goToTime, ignoreTime, animateToday) 172 next?.reloadEvents() 173 mViewSwitcher?.showNext() 174 next?.requestFocus() 175 next?.updateTitle() 176 next?.restartCurrentTimeUpdates() 177 } 178 } 179 180 /** 181 * Returns the selected time in milliseconds. The milliseconds are measured 182 * in UTC milliseconds from the epoch and uniquely specifies any selectable 183 * time. 184 * 185 * @return the selected time in milliseconds 186 */ 187 val selectedTimeInMillis: Long 188 get() { 189 if (mViewSwitcher == null) { 190 return -1 191 } 192 val view: DayView = mViewSwitcher?.getCurrentView() as DayView ?: return -1 193 return view.selectedTimeInMillis 194 } 195 eventsChangednull196 override fun eventsChanged() { 197 if (mViewSwitcher == null) { 198 return 199 } 200 var view: DayView? = mViewSwitcher?.getCurrentView() as? DayView 201 view?.clearCachedEvents() 202 view?.reloadEvents() 203 view = mViewSwitcher?.getNextView() as? DayView 204 view?.clearCachedEvents() 205 } 206 207 val nextView: DayView? 208 get() = mViewSwitcher?.getNextView() as? DayView 209 override val supportedEventTypes: Long 210 get() = CalendarController.EventType.GO_TO or CalendarController.EventType.EVENTS_CHANGED 211 handleEventnull212 override fun handleEvent(msg: CalendarController.EventInfo?) { 213 if (msg?.eventType == CalendarController.EventType.GO_TO) { 214 // TODO support a range of time 215 // TODO support event_id 216 // TODO support select message 217 goTo(msg.selectedTime, msg.extraLong and CalendarController.EXTRA_GOTO_DATE != 0L, 218 msg.extraLong and CalendarController.EXTRA_GOTO_TODAY != 0L) 219 } else if (msg?.eventType == CalendarController.EventType.EVENTS_CHANGED) { 220 eventsChanged() 221 } 222 } 223 224 companion object { 225 /** 226 * The view id used for all the views we create. It's OK to have all child 227 * views have the same ID. This ID is used to pick which view receives 228 * focus when a view hierarchy is saved / restore 229 */ 230 private const val VIEW_ID = 1 231 protected const val BUNDLE_KEY_RESTORE_TIME = "key_restore_time" 232 } 233 }