AgendaFragment.java revision 96d6163c524b338307b01d25916ccffd495e2f29
1/* 2 * Copyright (C) 2007 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 17package com.android.calendar.agenda; 18 19 20import android.app.Activity; 21import android.app.Fragment; 22import android.app.FragmentManager; 23import android.app.FragmentTransaction; 24import android.content.SharedPreferences; 25import android.os.Bundle; 26import android.text.format.Time; 27import android.util.Log; 28import android.view.LayoutInflater; 29import android.view.View; 30import android.view.ViewGroup; 31import android.widget.AbsListView; 32import android.widget.AbsListView.OnScrollListener; 33import android.widget.Adapter; 34import android.widget.HeaderViewListAdapter; 35 36import com.android.calendar.CalendarController; 37import com.android.calendar.CalendarController.EventInfo; 38import com.android.calendar.CalendarController.EventType; 39import com.android.calendar.CalendarController.ViewType; 40import com.android.calendar.EventInfoFragment; 41import com.android.calendar.GeneralPreferences; 42import com.android.calendar.R; 43import com.android.calendar.StickyHeaderListView; 44import com.android.calendar.StickyHeaderListView.HeaderIndexer; 45import com.android.calendar.Utils; 46 47 48public class AgendaFragment extends Fragment implements CalendarController.EventHandler, 49 OnScrollListener { 50 51 private static final String TAG = AgendaFragment.class.getSimpleName(); 52 private static boolean DEBUG = false; 53 54 protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time"; 55 protected static final String BUNDLE_KEY_RESTORE_INSTANCE_ID = "key_restore_instance_id"; 56 57 private AgendaListView mAgendaListView; 58 private Activity mActivity; 59 private Time mTime; 60 private String mTimeZone; 61 private long mInitialTimeMillis; 62 private boolean mShowEventDetailsWithAgenda; 63 private CalendarController mController; 64 private EventInfoFragment mEventFragment; 65 private String mQuery; 66 private boolean mUsedForSearch = false; 67 private boolean mIsTabletConfig; 68 private EventInfo mOnAttachedInfo = null; 69 70 // Tracks the time of the top visible view in order to send UPDATE_TITLE messages to the action 71 // bar. 72 int mJulianDayOnTop = -1; 73 74 private Runnable mTZUpdater = new Runnable() { 75 @Override 76 public void run() { 77 mTimeZone = Utils.getTimeZone(getActivity(), this); 78 mTime.switchTimezone(mTimeZone); 79 } 80 }; 81 82 public AgendaFragment() { 83 this(0, false); 84 } 85 86 87 // timeMillis - time of first event to show 88 // usedForSearch - indicates if this fragment is used in the search fragment 89 public AgendaFragment(long timeMillis, boolean usedForSearch) { 90 mInitialTimeMillis = timeMillis; 91 mTime = new Time(); 92 if (mInitialTimeMillis == 0) { 93 mTime.setToNow(); 94 } else { 95 mTime.set(mInitialTimeMillis); 96 } 97 mUsedForSearch = usedForSearch; 98 } 99 100 @Override 101 public void onAttach(Activity activity) { 102 super.onAttach(activity); 103 mTimeZone = Utils.getTimeZone(activity, mTZUpdater); 104 mTime.switchTimezone(mTimeZone); 105 mActivity = activity; 106 if (mOnAttachedInfo != null) { 107 showEventInfo(mOnAttachedInfo); 108 mOnAttachedInfo = null; 109 } 110 } 111 112 @Override 113 public void onCreate(Bundle icicle) { 114 super.onCreate(icicle); 115 mController = CalendarController.getInstance(mActivity); 116 mShowEventDetailsWithAgenda = 117 Utils.getConfigBool(mActivity, R.bool.show_event_details_with_agenda); 118 mIsTabletConfig = 119 Utils.getConfigBool(mActivity, R.bool.tablet_config); 120 if (icicle != null) { 121 long prevTime = icicle.getLong(BUNDLE_KEY_RESTORE_TIME, -1); 122 if (prevTime != -1) { 123 mTime.set(prevTime); 124 if (DEBUG) { 125 Log.d(TAG, "Restoring time to " + mTime.toString()); 126 } 127 } 128 } 129 } 130 131 @Override 132 public View onCreateView(LayoutInflater inflater, ViewGroup container, 133 Bundle savedInstanceState) { 134 135 136 View v = inflater.inflate(R.layout.agenda_fragment, null); 137 138 mAgendaListView = (AgendaListView)v.findViewById(R.id.agenda_events_list); 139 mAgendaListView.setClickable(true); 140 mAgendaListView.goTo(mTime, -1, mQuery, true); 141 142 if (savedInstanceState != null) { 143 long instanceId = savedInstanceState.getLong(BUNDLE_KEY_RESTORE_INSTANCE_ID, -1); 144 if (instanceId != -1) { 145 mAgendaListView.setSelectedInstanceId(instanceId); 146 } 147 } 148 149 if (!mShowEventDetailsWithAgenda) { 150 v.findViewById(R.id.agenda_event_info).setVisibility(View.GONE); 151 } 152 153 // Set adapter & HeaderIndexer for StickyHeaderListView 154 StickyHeaderListView lv = 155 (StickyHeaderListView)v.findViewById(R.id.agenda_sticky_header_list); 156 if (lv != null) { 157 Adapter a = mAgendaListView.getAdapter(); 158 lv.setAdapter(a); 159 if (a instanceof HeaderViewListAdapter) { 160 lv.setIndexer((HeaderIndexer) ((HeaderViewListAdapter)a).getWrappedAdapter()); 161 } else if (a instanceof AgendaWindowAdapter) { 162 lv.setIndexer((HeaderIndexer) a); 163 } else { 164 Log.wtf(TAG, "Cannot find HeaderIndexer for StickyHeaderListView"); 165 } 166 167 // Set scroll listener so that the date on the ActionBar can be set while 168 // the user scrolls the view 169 lv.setOnScrollListener(this); 170 } 171 return v; 172 } 173 174 @Override 175 public void onResume() { 176 super.onResume(); 177 if (DEBUG) { 178 Log.v(TAG, "OnResume to " + mTime.toString()); 179 } 180 181 SharedPreferences prefs = GeneralPreferences.getSharedPreferences( 182 getActivity()); 183 boolean hideDeclined = prefs.getBoolean( 184 GeneralPreferences.KEY_HIDE_DECLINED, false); 185 186 mAgendaListView.setHideDeclinedEvents(hideDeclined); 187 mAgendaListView.goTo(mTime, -1, mQuery, true); 188 mAgendaListView.onResume(); 189 190// // Register for Intent broadcasts 191// IntentFilter filter = new IntentFilter(); 192// filter.addAction(Intent.ACTION_TIME_CHANGED); 193// filter.addAction(Intent.ACTION_DATE_CHANGED); 194// filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 195// registerReceiver(mIntentReceiver, filter); 196// 197// mContentResolver.registerContentObserver(Events.CONTENT_URI, true, mObserver); 198 } 199 200 @Override 201 public void onSaveInstanceState(Bundle outState) { 202 super.onSaveInstanceState(outState); 203 if (mAgendaListView == null) { 204 return; 205 } 206 long firstVisibleTime = mController.getTime(); 207 if (firstVisibleTime > 0) { 208 mTime.set(firstVisibleTime); 209 outState.putLong(BUNDLE_KEY_RESTORE_TIME, firstVisibleTime); 210 if (DEBUG) { 211 Log.v(TAG, "onSaveInstanceState " + mTime.toString()); 212 } 213 } 214 215 long selectedInstance = mAgendaListView.getSelectedInstanceId(); 216 if (selectedInstance >= 0) { 217 outState.putLong(BUNDLE_KEY_RESTORE_INSTANCE_ID, selectedInstance); 218 } 219 } 220 221 /** 222 * This cleans up the event info fragment since the FragmentManager doesn't 223 * handle nested fragments. Without this, the action bar buttons added by 224 * the info fragment can come back on a rotation. 225 * 226 * @param fragmentManager 227 */ 228 public void removeFragments(FragmentManager fragmentManager) { 229 mController.deregisterEventHandler(R.id.agenda_event_info); 230 if (getActivity().isFinishing()) { 231 return; 232 } 233 FragmentTransaction ft = fragmentManager.beginTransaction(); 234 Fragment f = fragmentManager.findFragmentById(R.id.agenda_event_info); 235 if (f != null) { 236 ft.remove(f); 237 } 238 ft.commit(); 239 } 240 241 @Override 242 public void onPause() { 243 super.onPause(); 244 245 mAgendaListView.onPause(); 246// mContentResolver.unregisterContentObserver(mObserver); 247// unregisterReceiver(mIntentReceiver); 248 249 // Record Agenda View as the (new) default detailed view. 250// Utils.setDefaultView(this, CalendarApplication.AGENDA_VIEW_ID); 251 } 252 253 private void goTo(EventInfo event, boolean animate) { 254 if (mAgendaListView == null) { 255 // The view hasn't been set yet. Just save the time and use it 256 // later. 257 mTime.set(event.startTime); 258 return; 259 } 260 // Set mTime if we have a start time and we aren't in the range of the 261 // goto 262 if (event.startTime != null 263 && (mTime.before(event.startTime) || event.endTime == null || mTime 264 .after(event.endTime))) { 265 mTime.set(event.startTime); 266 } 267 mAgendaListView.goTo(mTime, event.id, mQuery, false); 268 showEventInfo(event); 269 } 270 271 private void search(String query, Time time) { 272 mQuery = query; 273 if (time != null) { 274 mTime.set(time); 275 } 276 if (mAgendaListView == null) { 277 // The view hasn't been set yet. Just return. 278 return; 279 } 280 mAgendaListView.goTo(time, -1, mQuery, true); 281 } 282 283 @Override 284 public void eventsChanged() { 285 if (mAgendaListView != null) { 286 mAgendaListView.refresh(true); 287 } 288 } 289 290 @Override 291 public long getSupportedEventTypes() { 292 return EventType.GO_TO | EventType.EVENTS_CHANGED | ((mUsedForSearch)?EventType.SEARCH:0); 293 } 294 295 @Override 296 public void handleEvent(EventInfo event) { 297 if (event.eventType == EventType.GO_TO) { 298 // TODO support a range of time 299 // TODO support event_id 300 // TODO figure out the animate bit 301 goTo(event, true); 302 } else if (event.eventType == EventType.SEARCH) { 303 search(event.query, event.startTime); 304 } else if (event.eventType == EventType.EVENTS_CHANGED) { 305 eventsChanged(); 306 } 307 } 308 309 310 // Shows the selected event in the Agenda view 311 private void showEventInfo(EventInfo event) { 312 313 // Ignore unknown events 314 if (event.id == -1) { 315 Log.e(TAG, "showEventInfo, event ID = " + event.id); 316 return; 317 } 318 319 // Create a fragment to show the event to the side of the agenda list 320 if (mShowEventDetailsWithAgenda) { 321 FragmentManager fragmentManager = getFragmentManager(); 322 if (fragmentManager == null) { 323 // Got a goto event before the fragment finished attaching, 324 // stash the event and handle it later. 325 mOnAttachedInfo = event; 326 return; 327 } 328 FragmentTransaction ft = fragmentManager.beginTransaction(); 329 int response = CalendarController.ATTENDEE_NO_RESPONSE; 330 if (event.eventType == EventType.VIEW_EVENT 331 || event.eventType == EventType.EDIT_EVENT) { 332 response = (int) event.extraLong; 333 } 334 mEventFragment = new EventInfoFragment(mActivity, event.id, 335 event.startTime.toMillis(false), event.endTime.toMillis(false), 336 response, false); 337 ft.replace(R.id.agenda_event_info, mEventFragment); 338 mController.registerEventHandler(R.id.agenda_event_info, 339 mEventFragment); 340 ft.commit(); 341 } 342// else { 343// Intent intent = new Intent(Intent.ACTION_VIEW); 344// Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, event.id); 345// intent.setData(eventUri); 346// intent.setClass(getActivity(), AllInOneActivity.class); 347// intent.putExtra(EVENT_BEGIN_TIME, event.startTime != null ? event.startTime 348// .toMillis(true) : -1); 349// intent.putExtra(EVENT_END_TIME, event.endTime != null ? event.endTime.toMillis(true) 350// : -1); 351// startActivity(intent); 352// } 353 } 354 355 // OnScrollListener implementation to update the date on the pull-down menu of the app 356 357 public void onScrollStateChanged(AbsListView view, int scrollState) { 358 // Do nothing 359 } 360 361 // Gets the time of the first visible view. If it is a new time, send a message to update 362 // the time on the ActionBar 363 @Override 364 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 365 int totalItemCount) { 366 int julianDay = mAgendaListView.getJulianDayFromPosition(firstVisibleItem 367 - mAgendaListView.getHeaderViewsCount()); 368 // On error - leave the old view 369 if (julianDay == 0) { 370 return; 371 } 372 // If the day changed, update the ActionBar 373 if (mJulianDayOnTop != julianDay) { 374 mJulianDayOnTop = julianDay; 375 Time t = new Time(mTimeZone); 376 t.setJulianDay(mJulianDayOnTop); 377 mController.setTime(t.toMillis(true)); 378 // Cannot sent a message that eventually may change the layout of the views 379 // so instead post a runnable that will run when the layout is done 380 if (!mIsTabletConfig) { 381 view.post(new Runnable() { 382 @Override 383 public void run() { 384 Time t = new Time(mTimeZone); 385 t.setJulianDay(mJulianDayOnTop); 386 mController.sendEvent(this, EventType.UPDATE_TITLE, t, t, null, -1, 387 ViewType.CURRENT, 0, null, null); 388 } 389 }); 390 } 391 } 392 } 393} 394