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