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