• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.accounts.AccountManager
19 import android.accounts.AccountManagerCallback
20 import android.accounts.AccountManagerFuture
21 import android.animation.Animator
22 import android.animation.Animator.AnimatorListener
23 import android.animation.ObjectAnimator
24 import android.app.ActionBar
25 import android.app.ActionBar.Tab
26 import android.app.Activity
27 import android.app.Fragment
28 import android.app.FragmentManager
29 import android.app.FragmentTransaction
30 import android.content.AsyncQueryHandler
31 import android.content.ContentResolver
32 import android.content.Intent
33 import android.content.SharedPreferences
34 import android.content.SharedPreferences.OnSharedPreferenceChangeListener
35 import android.content.res.Configuration
36 import android.content.res.Resources
37 import android.database.ContentObserver
38 import android.database.Cursor
39 import android.graphics.drawable.LayerDrawable
40 import android.net.Uri
41 import android.os.Bundle
42 import android.os.Handler
43 import android.provider.CalendarContract
44 import android.provider.CalendarContract.Attendees
45 import android.provider.CalendarContract.Calendars
46 import android.provider.CalendarContract.Events
47 import android.text.TextUtils
48 import android.text.format.DateFormat
49 import android.text.format.DateUtils
50 import android.text.format.Time
51 import android.util.Log
52 import android.view.Menu
53 import android.view.MenuItem
54 import android.view.View
55 import android.view.accessibility.AccessibilityEvent
56 import android.widget.LinearLayout
57 import android.widget.RelativeLayout
58 import android.widget.RelativeLayout.LayoutParams
59 import android.widget.TextView
60 import com.android.calendar.CalendarController.EventHandler
61 import com.android.calendar.CalendarController.EventInfo
62 import com.android.calendar.CalendarController.EventType
63 import com.android.calendar.CalendarController.ViewType
64 import com.android.calendar.month.MonthByWeekFragment
65 import java.util.Locale
66 import java.util.TimeZone
67 import android.provider.CalendarContract.Attendees.ATTENDEE_STATUS
68 import android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY
69 import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME
70 import android.provider.CalendarContract.EXTRA_EVENT_END_TIME
71 
72 class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListener,
73     ActionBar.TabListener, ActionBar.OnNavigationListener {
74     private var mController: CalendarController? = null
75     private var mOnSaveInstanceStateCalled = false
76     private var mBackToPreviousView = false
77     private var mContentResolver: ContentResolver? = null
78     private var mPreviousView = 0
79     private var mCurrentView = 0
80     private var mPaused = true
81     private var mUpdateOnResume = false
82     private var mHideControls = false
83     private var mShowSideViews = true
84     private var mShowWeekNum = false
85     private var mHomeTime: TextView? = null
86     private var mDateRange: TextView? = null
87     private var mWeekTextView: TextView? = null
88     private var mMiniMonth: View? = null
89     private var mCalendarsList: View? = null
90     private var mMiniMonthContainer: View? = null
91     private var mSecondaryPane: View? = null
92     private var mTimeZone: String? = null
93     private var mShowCalendarControls = false
94     private var mShowEventInfoFullScreen = false
95     private var mWeekNum = 0
96     private var mCalendarControlsAnimationTime = 0
97     private var mControlsAnimateWidth = 0
98     private var mControlsAnimateHeight = 0
99     private var mViewEventId: Long = -1
100     private var mIntentEventStartMillis: Long = -1
101     private var mIntentEventEndMillis: Long = -1
102     private var mIntentAttendeeResponse: Int = Attendees.ATTENDEE_STATUS_NONE
103     private var mIntentAllDay = false
104 
105     // Action bar and Navigation bar (left side of Action bar)
106     private var mActionBar: ActionBar? = null
107     private val mDayTab: Tab? = null
108     private val mWeekTab: Tab? = null
109     private val mMonthTab: Tab? = null
110     private var mControlsMenu: MenuItem? = null
111     private var mOptionsMenu: Menu? = null
112     private var mActionBarMenuSpinnerAdapter: CalendarViewAdapter? = null
113     private var mHandler: QueryHandler? = null
114     private var mCheckForAccounts = true
115     private var mHideString: String? = null
116     private var mShowString: String? = null
117     var mDayOfMonthIcon: DayOfMonthDrawable? = null
118     var mOrientation = 0
119 
120     // Params for animating the controls on the right
121     private var mControlsParams: LayoutParams? = null
122     private var mVerticalControlsParams: LinearLayout.LayoutParams? = null
123     private val mSlideAnimationDoneListener: AnimatorListener = object : AnimatorListener {
124         @Override
onAnimationCancelnull125         override fun onAnimationCancel(animation: Animator) {
126         }
127 
128         @Override
onAnimationEndnull129         override fun onAnimationEnd(animation: Animator) {
130             val visibility: Int = if (mShowSideViews) View.VISIBLE else View.GONE
131             mMiniMonth?.setVisibility(visibility)
132             mCalendarsList?.setVisibility(visibility)
133             mMiniMonthContainer?.setVisibility(visibility)
134         }
135 
136         @Override
onAnimationRepeatnull137         override fun onAnimationRepeat(animation: Animator) {
138         }
139 
140         @Override
onAnimationStartnull141         override fun onAnimationStart(animation: Animator) {
142         }
143     }
144 
145     private inner class QueryHandler(cr: ContentResolver?) : AsyncQueryHandler(cr) {
146         @Override
onQueryCompletenull147         protected override fun onQueryComplete(token: Int, cookie: Any?, cursor: Cursor?) {
148             mCheckForAccounts = false
149             try {
150                 // If the query didn't return a cursor for some reason return
151                 if (cursor == null || cursor.getCount() > 0 || isFinishing()) {
152                     return
153                 }
154             } finally {
155                 if (cursor != null) {
156                     cursor.close()
157                 }
158             }
159             val options = Bundle()
160             options.putCharSequence(
161                 "introMessage",
162                 getResources().getString(R.string.create_an_account_desc)
163             )
164             options.putBoolean("allowSkip", true)
165             val am: AccountManager = AccountManager.get(this@AllInOneActivity)
166             am.addAccount("com.google", CalendarContract.AUTHORITY, null, options,
167                 this@AllInOneActivity,
168                     object : AccountManagerCallback<Bundle?> {
169                         @Override
170                         override fun run(future: AccountManagerFuture<Bundle?>?) {
171                         }
172                     }, null
173             )
174         }
175     }
176 
177     private val mHomeTimeUpdater: Runnable = object : Runnable {
178         @Override
runnull179         override fun run() {
180             mTimeZone = Utils.getTimeZone(this@AllInOneActivity, this)
181             updateSecondaryTitleFields(-1)
182             this@AllInOneActivity.invalidateOptionsMenu()
183             Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone)
184         }
185     }
186 
187     // runs every midnight/time changes and refreshes the today icon
188     private val mTimeChangesUpdater: Runnable = object : Runnable {
189         @Override
runnull190         override fun run() {
191             mTimeZone = Utils.getTimeZone(this@AllInOneActivity, mHomeTimeUpdater)
192             this@AllInOneActivity.invalidateOptionsMenu()
193             Utils.setMidnightUpdater(mHandler, this, mTimeZone)
194         }
195     }
196 
197     // Create an observer so that we can update the views whenever a
198     // Calendar event changes.
199     private val mObserver: ContentObserver = object : ContentObserver(Handler()) {
200         @Override
deliverSelfNotificationsnull201         override fun deliverSelfNotifications(): Boolean {
202             return true
203         }
204 
205         @Override
onChangenull206         override fun onChange(selfChange: Boolean) {
207             eventsChanged()
208         }
209     }
210 
211     @Override
onNewIntentnull212     protected override fun onNewIntent(intent: Intent) {
213         val action: String? = intent.getAction()
214         if (DEBUG) Log.d(TAG, "New intent received " + intent.toString())
215         // Don't change the date if we're just returning to the app's home
216         if (Intent.ACTION_VIEW.equals(action) &&
217             !intent.getBooleanExtra(Utils.INTENT_KEY_HOME, false)
218         ) {
219             var millis = parseViewAction(intent)
220             if (millis == -1L) {
221                 millis = Utils.timeFromIntentInMillis(intent) as Long
222             }
223             if (millis != -1L && mViewEventId == -1L && mController != null) {
224                 val time = Time(mTimeZone)
225                 time.set(millis)
226                 time.normalize(true)
227                 mController?.sendEvent(this as Object?, EventType.GO_TO, time, time, -1,
228                     ViewType.CURRENT)
229             }
230         }
231     }
232 
233     @Override
onCreatenull234     protected override fun onCreate(icicle: Bundle?) {
235         super.onCreate(icicle)
236         if (icicle != null && icicle.containsKey(BUNDLE_KEY_CHECK_ACCOUNTS)) {
237             mCheckForAccounts = icicle.getBoolean(BUNDLE_KEY_CHECK_ACCOUNTS)
238         }
239         // Launch add google account if this is first time and there are no
240         // accounts yet
241         if (mCheckForAccounts) {
242             mHandler = QueryHandler(this.getContentResolver())
243             mHandler?.startQuery(
244                 0, null, Calendars.CONTENT_URI, arrayOf<String>(
245                     Calendars._ID
246                 ), null, null /* selection args */, null /* sort order */
247             )
248         }
249 
250         // This needs to be created before setContentView
251         mController = CalendarController.getInstance(this)
252 
253         // Get time from intent or icicle
254         var timeMillis: Long = -1
255         var viewType = -1
256         val intent: Intent = getIntent()
257         if (icicle != null) {
258             timeMillis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME)
259             viewType = icicle.getInt(BUNDLE_KEY_RESTORE_VIEW, -1)
260         } else {
261             val action: String? = intent.getAction()
262             if (Intent.ACTION_VIEW.equals(action)) {
263                 // Open EventInfo later
264                 timeMillis = parseViewAction(intent)
265             }
266             if (timeMillis == -1L) {
267                 timeMillis = Utils.timeFromIntentInMillis(intent) as Long
268             }
269         }
270         if (viewType == -1 || viewType > ViewType.MAX_VALUE) {
271             viewType = Utils.getViewTypeFromIntentAndSharedPref(this)
272         }
273         mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater)
274         val t = Time(mTimeZone)
275         t.set(timeMillis)
276         if (DEBUG) {
277             if (icicle != null && intent != null) {
278                 Log.d(
279                     TAG,
280                     "both, icicle:" + icicle.toString().toString() + "  intent:" + intent.toString()
281                 )
282             } else {
283                 Log.d(TAG, "not both, icicle:$icicle intent:$intent")
284             }
285         }
286         val res: Resources = getResources()
287         mHideString = res.getString(R.string.hide_controls)
288         mShowString = res.getString(R.string.show_controls)
289         mOrientation = res.getConfiguration().orientation
290         if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) {
291             mControlsAnimateWidth = res.getDimension(R.dimen.calendar_controls_width).toInt()
292             if (mControlsParams == null) {
293                 mControlsParams = LayoutParams(mControlsAnimateWidth, 0)
294             }
295             mControlsParams?.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
296                 as RelativeLayout.LayoutParams
297         } else {
298             // Make sure width is in between allowed min and max width values
299             mControlsAnimateWidth = Math.max(
300                 res.getDisplayMetrics().widthPixels * 45 / 100,
301                 res.getDimension(R.dimen.min_portrait_calendar_controls_width).toInt()
302             )
303             mControlsAnimateWidth = Math.min(
304                 mControlsAnimateWidth,
305                 res.getDimension(R.dimen.max_portrait_calendar_controls_width).toInt()
306             )
307         }
308         mControlsAnimateHeight = res.getDimension(R.dimen.calendar_controls_height).toInt()
309         mHideControls = true
310         mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config)
311         mIsTabletConfig = Utils.getConfigBool(this, R.bool.tablet_config)
312         mShowCalendarControls = Utils.getConfigBool(this, R.bool.show_calendar_controls)
313         mShowEventInfoFullScreen = Utils.getConfigBool(this, R.bool.show_event_info_full_screen)
314         mCalendarControlsAnimationTime = res.getInteger(R.integer.calendar_controls_animation_time)
315         Utils.setAllowWeekForDetailView(mIsMultipane)
316 
317         // setContentView must be called before configureActionBar
318         setContentView(R.layout.all_in_one)
319         if (mIsTabletConfig) {
320             mDateRange = findViewById(R.id.date_bar) as TextView?
321             mWeekTextView = findViewById(R.id.week_num) as TextView?
322         } else {
323             mDateRange = getLayoutInflater().inflate(R.layout.date_range_title, null) as TextView
324         }
325 
326         // configureActionBar auto-selects the first tab you add, so we need to
327         // call it before we set up our own fragments to make sure it doesn't
328         // overwrite us
329         configureActionBar(viewType)
330         mHomeTime = findViewById(R.id.home_time) as TextView?
331         mMiniMonth = findViewById(R.id.mini_month)
332         if (mIsTabletConfig && mOrientation == Configuration.ORIENTATION_PORTRAIT) {
333             mMiniMonth?.setLayoutParams(
334                 LayoutParams(
335                     mControlsAnimateWidth,
336                     mControlsAnimateHeight
337                 )
338             )
339         }
340         mCalendarsList = findViewById(R.id.calendar_list)
341         mMiniMonthContainer = findViewById(R.id.mini_month_container)
342         mSecondaryPane = findViewById(R.id.secondary_pane)
343 
344         // Must register as the first activity because this activity can modify
345         // the list of event handlers in it's handle method. This affects who
346         // the rest of the handlers the controller dispatches to are.
347         mController?.registerFirstEventHandler(HANDLER_KEY, this)
348         initFragments(timeMillis, viewType, icicle)
349 
350         // Listen for changes that would require this to be refreshed
351         val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(this)
352         prefs?.registerOnSharedPreferenceChangeListener(this)
353         mContentResolver = getContentResolver()
354     }
355 
parseViewActionnull356     private fun parseViewAction(intent: Intent?): Long {
357         var timeMillis: Long = -1
358         val data: Uri? = intent?.getData()
359         if (data != null && data.isHierarchical()) {
360             val path = data.getPathSegments()
361             if (path?.size == 2 && path[0].equals("events")) {
362                 try {
363                     mViewEventId = data.getLastPathSegment()?.toLong() as Long
364                     if (mViewEventId != -1L) {
365                         mIntentEventStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0)
366                         mIntentEventEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0)
367                         mIntentAttendeeResponse = intent.getIntExtra(
368                             ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE
369                         )
370                         mIntentAllDay = intent.getBooleanExtra(EXTRA_EVENT_ALL_DAY, false)
371                             as Boolean
372                         timeMillis = mIntentEventStartMillis
373                     }
374                 } catch (e: NumberFormatException) {
375                     // Ignore if mViewEventId can't be parsed
376                 }
377             }
378         }
379         return timeMillis
380     }
381 
configureActionBarnull382     private fun configureActionBar(viewType: Int) {
383         createButtonsSpinner(viewType, mIsTabletConfig)
384         if (mIsMultipane) {
385             mActionBar?.setDisplayOptions(
386                 ActionBar.DISPLAY_SHOW_CUSTOM or ActionBar.DISPLAY_SHOW_HOME
387             )
388         } else {
389             mActionBar?.setDisplayOptions(0)
390         }
391     }
392 
createButtonsSpinnernull393     private fun createButtonsSpinner(viewType: Int, tabletConfig: Boolean) {
394         // If tablet configuration , show spinner with no dates
395         mActionBarMenuSpinnerAdapter = CalendarViewAdapter(this, viewType, !tabletConfig)
396         mActionBar = getActionBar()
397         mActionBar?.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST)
398         mActionBar?.setListNavigationCallbacks(mActionBarMenuSpinnerAdapter, this)
399         when (viewType) {
400             ViewType.AGENDA -> {
401             }
402             ViewType.DAY -> mActionBar?.setSelectedNavigationItem(BUTTON_DAY_INDEX)
403             ViewType.WEEK -> mActionBar?.setSelectedNavigationItem(BUTTON_WEEK_INDEX)
404             ViewType.MONTH -> mActionBar?.setSelectedNavigationItem(BUTTON_MONTH_INDEX)
405             else -> mActionBar?.setSelectedNavigationItem(BUTTON_DAY_INDEX)
406         }
407     }
408 
409     // Clear buttons used in the agenda view
clearOptionsMenunull410     private fun clearOptionsMenu() {
411         if (mOptionsMenu == null) {
412             return
413         }
414         val cancelItem: MenuItem? = mOptionsMenu?.findItem(R.id.action_cancel)
415         if (cancelItem != null) {
416             cancelItem.setVisible(false)
417         }
418     }
419 
420     @Override
onResumenull421     protected override fun onResume() {
422         super.onResume()
423 
424         // Check if the upgrade code has ever been run. If not, force a sync just this one time.
425         Utils.trySyncAndDisableUpgradeReceiver(this)
426 
427         // Must register as the first activity because this activity can modify
428         // the list of event handlers in it's handle method. This affects who
429         // the rest of the handlers the controller dispatches to are.
430         mController?.registerFirstEventHandler(HANDLER_KEY, this)
431         mOnSaveInstanceStateCalled = false
432         mContentResolver?.registerContentObserver(
433             CalendarContract.Events.CONTENT_URI,
434             true, mObserver
435         )
436         if (mUpdateOnResume) {
437             initFragments(mController?.time as Long, mController?.viewType as Int, null)
438             mUpdateOnResume = false
439         }
440         val t = Time(mTimeZone)
441         t.set(mController?.time as Long)
442         mController?.sendEvent(
443             this as Object?, EventType.UPDATE_TITLE, t, t, -1, ViewType.CURRENT,
444             mController?.dateFlags as Long, null, null
445         )
446         // Make sure the drop-down menu will get its date updated at midnight
447         if (mActionBarMenuSpinnerAdapter != null) {
448             mActionBarMenuSpinnerAdapter?.refresh(this)
449         }
450         if (mControlsMenu != null) {
451             mControlsMenu?.setTitle(if (mHideControls) mShowString else mHideString)
452         }
453         mPaused = false
454         if (mViewEventId != -1L && mIntentEventStartMillis != -1L && mIntentEventEndMillis != -1L) {
455             val currentMillis: Long = System.currentTimeMillis()
456             var selectedTime: Long = -1
457             if (currentMillis > mIntentEventStartMillis && currentMillis < mIntentEventEndMillis) {
458                 selectedTime = currentMillis
459             }
460             mController?.sendEventRelatedEventWithExtra(
461                 this as Object?, EventType.VIEW_EVENT, mViewEventId,
462                 mIntentEventStartMillis, mIntentEventEndMillis, -1, -1,
463                 EventInfo.buildViewExtraLong(mIntentAttendeeResponse, mIntentAllDay),
464                 selectedTime
465             )
466             mViewEventId = -1
467             mIntentEventStartMillis = -1
468             mIntentEventEndMillis = -1
469             mIntentAllDay = false
470         }
471         Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone)
472         // Make sure the today icon is up to date
473         invalidateOptionsMenu()
474     }
475 
476     @Override
onPausenull477     protected override fun onPause() {
478         super.onPause()
479         mController?.deregisterEventHandler(HANDLER_KEY)
480         mPaused = true
481         mHomeTime?.removeCallbacks(mHomeTimeUpdater)
482         if (mActionBarMenuSpinnerAdapter != null) {
483             mActionBarMenuSpinnerAdapter?.onPause()
484         }
485         mContentResolver?.unregisterContentObserver(mObserver)
486         if (isFinishing()) {
487             // Stop listening for changes that would require this to be refreshed
488             val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(this)
489             prefs?.unregisterOnSharedPreferenceChangeListener(this)
490         }
491         // FRAG_TODO save highlighted days of the week;
492         if (mController?.viewType != ViewType.EDIT) {
493             Utils.setDefaultView(this, mController?.viewType as Int)
494         }
495         Utils.resetMidnightUpdater(mHandler, mTimeChangesUpdater)
496     }
497 
498     @Override
onUserLeaveHintnull499     protected override fun onUserLeaveHint() {
500         mController?.sendEvent(this as Object?, EventType.USER_HOME, null, null, -1,
501             ViewType.CURRENT)
502         super.onUserLeaveHint()
503     }
504 
505     @Override
onSaveInstanceStatenull506     override fun onSaveInstanceState(outState: Bundle) {
507         mOnSaveInstanceStateCalled = true
508         super.onSaveInstanceState(outState)
509     }
510 
511     @Override
onDestroynull512     protected override fun onDestroy() {
513         super.onDestroy()
514         val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(this)
515         prefs?.unregisterOnSharedPreferenceChangeListener(this)
516         mController?.deregisterAllEventHandlers()
517         CalendarController.removeInstance(this)
518     }
519 
initFragmentsnull520     private fun initFragments(timeMillis: Long, viewType: Int, icicle: Bundle?) {
521         if (DEBUG) {
522             Log.d(TAG, "Initializing to $timeMillis for view $viewType")
523         }
524         val ft: FragmentTransaction = getFragmentManager().beginTransaction()
525         if (mShowCalendarControls) {
526             val miniMonthFrag: Fragment = MonthByWeekFragment(timeMillis, true)
527             ft.replace(R.id.mini_month, miniMonthFrag)
528             mController?.registerEventHandler(R.id.mini_month, miniMonthFrag as EventHandler)
529         }
530         if (!mShowCalendarControls || viewType == ViewType.EDIT) {
531             mMiniMonth?.setVisibility(View.GONE)
532             mCalendarsList?.setVisibility(View.GONE)
533         }
534         var info: EventInfo? = null
535         if (viewType == ViewType.EDIT) {
536             mPreviousView = GeneralPreferences.getSharedPreferences(this)?.getInt(
537                 GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW
538             ) as Int
539             var eventId: Long = -1
540             val intent: Intent = getIntent()
541             val data: Uri? = intent.getData()
542             if (data != null) {
543                 try {
544                     eventId = data.getLastPathSegment()?.toLong() as Long
545                 } catch (e: NumberFormatException) {
546                     if (DEBUG) {
547                         Log.d(TAG, "Create new event")
548                     }
549                 }
550             } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) {
551                 eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID)
552             }
553             val begin: Long = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1)
554             val end: Long = intent.getLongExtra(EXTRA_EVENT_END_TIME, -1)
555             info = EventInfo()
556             if (end != -1L) {
557                 info.endTime = Time()
558                 info.endTime?.set(end)
559             }
560             if (begin != -1L) {
561                 info.startTime = Time()
562                 info.startTime?.set(begin)
563             }
564             info.id = eventId
565             // We set the viewtype so if the user presses back when they are
566             // done editing the controller knows we were in the Edit Event
567             // screen. Likewise for eventId
568             mController?.viewType = viewType
569             mController?.eventId = eventId
570         } else {
571             mPreviousView = viewType
572         }
573         setMainPane(ft, R.id.main_pane, viewType, timeMillis, true)
574         ft.commit() // this needs to be after setMainPane()
575         val t = Time(mTimeZone)
576         t.set(timeMillis)
577         if (viewType != ViewType.EDIT) {
578             mController?.sendEvent(this as Object?, EventType.GO_TO, t, null, -1, viewType)
579         }
580     }
581 
582     @Override
onBackPressednull583     override fun onBackPressed() {
584         if (mCurrentView == ViewType.EDIT || mBackToPreviousView) {
585             mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, mPreviousView)
586         } else {
587             super.onBackPressed()
588         }
589     }
590 
591     @Override
onCreateOptionsMenunull592     override fun onCreateOptionsMenu(menu: Menu): Boolean {
593         super.onCreateOptionsMenu(menu)
594         mOptionsMenu = menu
595         getMenuInflater().inflate(R.menu.all_in_one_title_bar, menu)
596 
597         // Hide the "show/hide controls" button if this is a phone
598         // or the view type is "Month".
599         mControlsMenu = menu.findItem(R.id.action_hide_controls)
600         if (!mShowCalendarControls) {
601             if (mControlsMenu != null) {
602                 mControlsMenu?.setVisible(false)
603                 mControlsMenu?.setEnabled(false)
604             }
605         } else if (mControlsMenu != null && mController != null &&
606             mController?.viewType == ViewType.MONTH) {
607             mControlsMenu?.setVisible(false)
608             mControlsMenu?.setEnabled(false)
609         } else if (mControlsMenu != null) {
610             mControlsMenu?.setTitle(if (mHideControls) mShowString else mHideString)
611         }
612         val menuItem: MenuItem = menu.findItem(R.id.action_today)
613         if (Utils.isJellybeanOrLater()) {
614             // replace the default top layer drawable of the today icon with a
615             // custom drawable that shows the day of the month of today
616             val icon: LayerDrawable = menuItem.getIcon() as LayerDrawable
617             Utils.setTodayIcon(icon, this, mTimeZone)
618         } else {
619             menuItem.setIcon(R.drawable.ic_menu_today_no_date_holo_light)
620         }
621         return true
622     }
623 
624     @Override
onOptionsItemSelectednull625     override fun onOptionsItemSelected(item: MenuItem): Boolean {
626         var t: Time? = null
627         var viewType: Int = ViewType.CURRENT
628         var extras: Long = CalendarController.EXTRA_GOTO_TIME
629         val itemId: Int = item.getItemId()
630         if (itemId == R.id.action_today) {
631             viewType = ViewType.CURRENT
632             t = Time(mTimeZone)
633             t.setToNow()
634             extras = extras or CalendarController.EXTRA_GOTO_TODAY
635         } else if (itemId == R.id.action_hide_controls) {
636             mHideControls = !mHideControls
637             item.setTitle(if (mHideControls) mShowString else mHideString)
638             if (!mHideControls) {
639                 mMiniMonth?.setVisibility(View.VISIBLE)
640                 mCalendarsList?.setVisibility(View.VISIBLE)
641                 mMiniMonthContainer?.setVisibility(View.VISIBLE)
642             }
643             val slideAnimation: ObjectAnimator = ObjectAnimator.ofInt(
644                 this, "controlsOffset",
645                 if (mHideControls) 0 else mControlsAnimateWidth,
646                 if (mHideControls) mControlsAnimateWidth else 0
647             )
648             slideAnimation.setDuration(mCalendarControlsAnimationTime.toLong())
649             ObjectAnimator.setFrameDelay(0)
650             slideAnimation.start()
651             return true
652         } else {
653             Log.d(TAG, "Unsupported itemId: $itemId")
654             return true
655         }
656         mController?.sendEvent(this as Object?, EventType.GO_TO, t, null, t, -1,
657             viewType, extras, null, null)
658         return true
659     }
660 
661     /**
662      * Sets the offset of the controls on the right for animating them off/on
663      * screen. ProGuard strips this if it's not in proguard.flags
664      *
665      * @param controlsOffset The current offset in pixels
666      */
setControlsOffsetnull667     fun setControlsOffset(controlsOffset: Int) {
668         if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) {
669             mMiniMonth?.setTranslationX(controlsOffset.toFloat())
670             mCalendarsList?.setTranslationX(controlsOffset.toFloat())
671             mControlsParams?.width = Math.max(0, mControlsAnimateWidth - controlsOffset)
672             mMiniMonthContainer?.setLayoutParams(mControlsParams)
673         } else {
674             mMiniMonth?.setTranslationY(controlsOffset.toFloat())
675             mCalendarsList?.setTranslationY(controlsOffset.toFloat())
676             if (mVerticalControlsParams == null) {
677                 mVerticalControlsParams = LayoutParams(
678                     LinearLayout.LayoutParams.MATCH_PARENT, mControlsAnimateHeight
679                 ) as LinearLayout.LayoutParams?
680             }
681             mVerticalControlsParams?.height = Math.max(0, mControlsAnimateHeight - controlsOffset)
682             mMiniMonthContainer?.setLayoutParams(mVerticalControlsParams)
683         }
684     }
685 
686     @Override
onSharedPreferenceChangednull687     override fun onSharedPreferenceChanged(prefs: SharedPreferences?, key: String) {
688         if (key.equals(GeneralPreferences.KEY_WEEK_START_DAY)) {
689             if (mPaused) {
690                 mUpdateOnResume = true
691             } else {
692                 initFragments(mController?.time as Long, mController?.viewType as Int, null)
693             }
694         }
695     }
696 
setMainPanenull697     private fun setMainPane(
698         ft: FragmentTransaction?,
699         viewId: Int,
700         viewType: Int,
701         timeMillis: Long,
702         force: Boolean
703     ) {
704         var ft: FragmentTransaction? = ft
705         if (mOnSaveInstanceStateCalled) {
706             return
707         }
708         if (!force && mCurrentView == viewType) {
709             return
710         }
711 
712         // Remove this when transition to and from month view looks fine.
713         val doTransition = viewType != ViewType.MONTH && mCurrentView != ViewType.MONTH
714         val fragmentManager: FragmentManager = getFragmentManager()
715         if (viewType != mCurrentView) {
716             // The rules for this previous view are different than the
717             // controller's and are used for intercepting the back button.
718             if (mCurrentView != ViewType.EDIT && mCurrentView > 0) {
719                 mPreviousView = mCurrentView
720             }
721             mCurrentView = viewType
722         }
723         // Create new fragment
724         var frag: Fragment? = null
725         val secFrag: Fragment? = null
726         when (viewType) {
727             ViewType.AGENDA -> {
728             }
729             ViewType.DAY -> {
730                 if (mActionBar != null && mActionBar?.getSelectedTab() != mDayTab) {
731                     mActionBar?.selectTab(mDayTab)
732                 }
733                 if (mActionBarMenuSpinnerAdapter != null) {
734                     mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.DAY_BUTTON_INDEX)
735                 }
736                 frag = DayFragment(timeMillis, 1)
737             }
738             ViewType.MONTH -> {
739                 if (mActionBar != null && mActionBar?.getSelectedTab() != mMonthTab) {
740                     mActionBar?.selectTab(mMonthTab)
741                 }
742                 if (mActionBarMenuSpinnerAdapter != null) {
743                     mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.MONTH_BUTTON_INDEX)
744                 }
745                 frag = MonthByWeekFragment(timeMillis, false)
746             }
747             ViewType.WEEK -> {
748                 if (mActionBar != null && mActionBar?.getSelectedTab() != mWeekTab) {
749                     mActionBar?.selectTab(mWeekTab)
750                 }
751                 if (mActionBarMenuSpinnerAdapter != null) {
752                     mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX)
753                 }
754                 frag = DayFragment(timeMillis, 7)
755             }
756             else -> {
757                 if (mActionBar != null && mActionBar?.getSelectedTab() != mWeekTab) {
758                     mActionBar?.selectTab(mWeekTab)
759                 }
760                 if (mActionBarMenuSpinnerAdapter != null) {
761                     mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX)
762                 }
763                 frag = DayFragment(timeMillis, 7)
764             }
765         }
766 
767         // Update the current view so that the menu can update its look according to the
768         // current view.
769         if (mActionBarMenuSpinnerAdapter != null) {
770             mActionBarMenuSpinnerAdapter?.setMainView(viewType)
771             if (!mIsTabletConfig) {
772                 mActionBarMenuSpinnerAdapter?.setTime(timeMillis)
773             }
774         }
775 
776         // Show date only on tablet configurations in views different than Agenda
777         if (!mIsTabletConfig) {
778             mDateRange?.setVisibility(View.GONE)
779         } else {
780             mDateRange?.setVisibility(View.GONE)
781         }
782 
783         // Clear unnecessary buttons from the option menu when switching from the agenda view
784         if (viewType != ViewType.AGENDA) {
785             clearOptionsMenu()
786         }
787         var doCommit = false
788         if (ft == null) {
789             doCommit = true
790             ft = fragmentManager.beginTransaction()
791         }
792         if (doTransition) {
793             ft?.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
794         }
795         ft?.replace(viewId, frag)
796         if (DEBUG) {
797             Log.d(TAG, "Adding handler with viewId $viewId and type $viewType")
798         }
799         // If the key is already registered this will replace it
800         mController?.registerEventHandler(viewId, frag as EventHandler?)
801         if (doCommit) {
802             if (DEBUG) {
803                 Log.d(TAG, "setMainPane AllInOne=" + this + " finishing:" + this.isFinishing())
804             }
805             ft?.commit()
806         }
807     }
808 
setTitleInActionBarnull809     private fun setTitleInActionBar(event: EventInfo) {
810         if (event.eventType != EventType.UPDATE_TITLE || mActionBar == null) {
811             return
812         }
813         val start: Long? = event.startTime?.toMillis(false /* use isDst */)
814         val end: Long?
815         end = if (event.endTime != null) {
816             event.endTime?.toMillis(false /* use isDst */)
817         } else {
818             start
819         }
820         val msg: String? = Utils.formatDateRange(this,
821             start as Long,
822             end as Long,
823             event.extraLong.toInt()
824         )
825         val oldDate: CharSequence? = mDateRange?.getText()
826         mDateRange?.setText(msg)
827         updateSecondaryTitleFields(if (event.selectedTime != null)
828             event.selectedTime?.toMillis(true) as Long else start)
829         if (!TextUtils.equals(oldDate, msg)) {
830             mDateRange?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
831             if (mShowWeekNum && mWeekTextView != null) {
832                 mWeekTextView?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
833             }
834         }
835     }
836 
updateSecondaryTitleFieldsnull837     private fun updateSecondaryTitleFields(visibleMillisSinceEpoch: Long) {
838         mShowWeekNum = Utils.getShowWeekNumber(this)
839         mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater)
840         if (visibleMillisSinceEpoch != -1L) {
841             val weekNum: Int = Utils.getWeekNumberFromTime(visibleMillisSinceEpoch, this)
842             mWeekNum = weekNum
843         }
844         if (mShowWeekNum && mCurrentView == ViewType.WEEK && mIsTabletConfig &&
845             mWeekTextView != null
846         ) {
847             val weekString: String = getResources().getQuantityString(
848                 R.plurals.weekN, mWeekNum,
849                 mWeekNum
850             )
851             mWeekTextView?.setText(weekString)
852             mWeekTextView?.setVisibility(View.VISIBLE)
853         } else if (visibleMillisSinceEpoch != -1L && mWeekTextView != null &&
854             mCurrentView == ViewType.DAY && mIsTabletConfig) {
855             val time = Time(mTimeZone)
856             time.set(visibleMillisSinceEpoch)
857             val julianDay: Int = Time.getJulianDay(visibleMillisSinceEpoch, time.gmtoff)
858             time.setToNow()
859             val todayJulianDay: Int = Time.getJulianDay(time.toMillis(false), time.gmtoff)
860             val dayString: String = Utils.getDayOfWeekString(
861                 julianDay,
862                 todayJulianDay,
863                 visibleMillisSinceEpoch,
864                 this
865             )
866             mWeekTextView?.setText(dayString)
867             mWeekTextView?.setVisibility(View.VISIBLE)
868         } else if (mWeekTextView != null && (!mIsTabletConfig || mCurrentView != ViewType.DAY)) {
869             mWeekTextView?.setVisibility(View.GONE)
870         }
871         if (mHomeTime != null && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK) &&
872             !TextUtils.equals(mTimeZone, Time.getCurrentTimezone())
873         ) {
874             val time = Time(mTimeZone)
875             time.setToNow()
876             val millis: Long = time.toMillis(true)
877             val isDST = time.isDst !== 0
878             var flags: Int = DateUtils.FORMAT_SHOW_TIME
879             if (DateFormat.is24HourFormat(this)) {
880                 flags = flags or DateUtils.FORMAT_24HOUR
881             }
882             // Formats the time as
883             val timeString: String = StringBuilder(
884                 Utils.formatDateRange(this, millis, millis, flags)
885             ).append(" ").append(
886                 TimeZone.getTimeZone(mTimeZone).getDisplayName(
887                     isDST, TimeZone.SHORT, Locale.getDefault()
888                 )
889             ).toString()
890             mHomeTime?.setText(timeString)
891             mHomeTime?.setVisibility(View.VISIBLE)
892             // Update when the minute changes
893             mHomeTime?.removeCallbacks(mHomeTimeUpdater)
894             mHomeTime?.postDelayed(
895                 mHomeTimeUpdater,
896                 DateUtils.MINUTE_IN_MILLIS - millis % DateUtils.MINUTE_IN_MILLIS
897             )
898         } else if (mHomeTime != null) {
899             mHomeTime?.setVisibility(View.GONE)
900         }
901     }
902 
903     @get:Override override val supportedEventTypes: Long
904         get() = EventType.GO_TO or EventType.UPDATE_TITLE
905 
906     @Override
handleEventnull907     override fun handleEvent(event: EventInfo?) {
908         var displayTime: Long = -1
909         if (event?.eventType == EventType.GO_TO) {
910             if (event.extraLong and CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS != 0L) {
911                 mBackToPreviousView = true
912             } else if (event.viewType != mController?.previousViewType &&
913                 event.viewType != ViewType.EDIT
914             ) {
915                 // Clear the flag is change to a different view type
916                 mBackToPreviousView = false
917             }
918             setMainPane(
919                 null, R.id.main_pane, event.viewType, event.startTime?.toMillis(false)
920                     as Long, false
921             )
922             if (mShowCalendarControls) {
923                 val animationSize =
924                     if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) mControlsAnimateWidth
925                     else mControlsAnimateHeight
926                 val noControlsView = event.viewType == ViewType.MONTH
927                 if (mControlsMenu != null) {
928                     mControlsMenu?.setVisible(!noControlsView)
929                     mControlsMenu?.setEnabled(!noControlsView)
930                 }
931                 if (noControlsView || mHideControls) {
932                     // hide minimonth and calendar frag
933                     mShowSideViews = false
934                     if (!mHideControls) {
935                         val slideAnimation: ObjectAnimator = ObjectAnimator.ofInt(
936                             this,
937                             "controlsOffset", 0, animationSize
938                         )
939                         slideAnimation.addListener(mSlideAnimationDoneListener)
940                         slideAnimation.setDuration(mCalendarControlsAnimationTime.toLong())
941                         ObjectAnimator.setFrameDelay(0)
942                         slideAnimation.start()
943                     } else {
944                         mMiniMonth?.setVisibility(View.GONE)
945                         mCalendarsList?.setVisibility(View.GONE)
946                         mMiniMonthContainer?.setVisibility(View.GONE)
947                     }
948                 } else {
949                     // show minimonth and calendar frag
950                     mShowSideViews = true
951                     mMiniMonth?.setVisibility(View.VISIBLE)
952                     mCalendarsList?.setVisibility(View.VISIBLE)
953                     mMiniMonthContainer?.setVisibility(View.VISIBLE)
954                     if (!mHideControls &&
955                         mController?.previousViewType == ViewType.MONTH
956                     ) {
957                         val slideAnimation: ObjectAnimator = ObjectAnimator.ofInt(
958                             this,
959                             "controlsOffset", animationSize, 0
960                         )
961                         slideAnimation.setDuration(mCalendarControlsAnimationTime.toLong())
962                         ObjectAnimator.setFrameDelay(0)
963                         slideAnimation.start()
964                     }
965                 }
966             }
967             displayTime =
968                 if (event.selectedTime != null) event.selectedTime?.toMillis(true) as Long
969                 else event.startTime?.toMillis(true) as Long
970             if (!mIsTabletConfig) {
971                 mActionBarMenuSpinnerAdapter?.setTime(displayTime)
972             }
973         } else if (event?.eventType == EventType.UPDATE_TITLE) {
974             setTitleInActionBar(event as CalendarController.EventInfo)
975             if (!mIsTabletConfig) {
976                 mActionBarMenuSpinnerAdapter?.setTime(mController?.time as Long)
977             }
978         }
979         updateSecondaryTitleFields(displayTime)
980     }
981 
982     @Override
eventsChangednull983     override fun eventsChanged() {
984         mController?.sendEvent(this as Object?, EventType.EVENTS_CHANGED, null, null, -1,
985             ViewType.CURRENT)
986     }
987 
988     @Override
onTabSelectednull989     override fun onTabSelected(tab: Tab?, ft: FragmentTransaction?) {
990         Log.w(TAG, "TabSelected AllInOne=" + this + " finishing:" + this.isFinishing())
991         if (tab == mDayTab && mCurrentView != ViewType.DAY) {
992             mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, ViewType.DAY)
993         } else if (tab == mWeekTab && mCurrentView != ViewType.WEEK) {
994             mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, ViewType.WEEK)
995         } else if (tab == mMonthTab && mCurrentView != ViewType.MONTH) {
996             mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, ViewType.MONTH)
997         } else {
998             Log.w(
999                 TAG, "TabSelected event from unknown tab: " +
1000                     if (tab == null) "null" else tab.getText()
1001             )
1002             Log.w(
1003                 TAG, "CurrentView:" + mCurrentView + " Tab:" + tab.toString() + " Day:" + mDayTab +
1004                     " Week:" + mWeekTab + " Month:" + mMonthTab
1005             )
1006         }
1007     }
1008 
1009     @Override
onTabReselectednull1010     override fun onTabReselected(tab: Tab?, ft: FragmentTransaction?) {
1011     }
1012 
1013     @Override
onTabUnselectednull1014     override fun onTabUnselected(tab: Tab?, ft: FragmentTransaction?) {
1015     }
1016 
1017     @Override
onNavigationItemSelectednull1018     override fun onNavigationItemSelected(itemPosition: Int, itemId: Long): Boolean {
1019         when (itemPosition) {
1020             CalendarViewAdapter.DAY_BUTTON_INDEX -> if (mCurrentView != ViewType.DAY) {
1021                 mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1,
1022                     ViewType.DAY)
1023             }
1024             CalendarViewAdapter.WEEK_BUTTON_INDEX -> if (mCurrentView != ViewType.WEEK) {
1025                 mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1,
1026                     ViewType.WEEK)
1027             }
1028             CalendarViewAdapter.MONTH_BUTTON_INDEX -> if (mCurrentView != ViewType.MONTH) {
1029                 mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1,
1030                     ViewType.MONTH)
1031             }
1032             CalendarViewAdapter.AGENDA_BUTTON_INDEX -> {
1033             }
1034             else -> {
1035                 Log.w(TAG, "ItemSelected event from unknown button: $itemPosition")
1036                 Log.w(
1037                     TAG, "CurrentView:" + mCurrentView + " Button:" + itemPosition +
1038                         " Day:" + mDayTab + " Week:" + mWeekTab + " Month:" + mMonthTab
1039                 )
1040             }
1041         }
1042         return false
1043     }
1044 
1045     companion object {
1046         private const val TAG = "AllInOneActivity"
1047         private const val DEBUG = false
1048         private const val EVENT_INFO_FRAGMENT_TAG = "EventInfoFragment"
1049         private const val BUNDLE_KEY_RESTORE_TIME = "key_restore_time"
1050         private const val BUNDLE_KEY_EVENT_ID = "key_event_id"
1051         private const val BUNDLE_KEY_RESTORE_VIEW = "key_restore_view"
1052         private const val BUNDLE_KEY_CHECK_ACCOUNTS = "key_check_for_accounts"
1053         private const val HANDLER_KEY = 0
1054 
1055         // Indices of buttons for the drop down menu (tabs replacement)
1056         // Must match the strings in the array buttons_list in arrays.xml and the
1057         // OnNavigationListener
1058         private const val BUTTON_DAY_INDEX = 0
1059         private const val BUTTON_WEEK_INDEX = 1
1060         private const val BUTTON_MONTH_INDEX = 2
1061         private const val BUTTON_AGENDA_INDEX = 3
1062         private var mIsMultipane = false
1063         private var mIsTabletConfig = false
1064     }
1065 }