• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.calendar.agenda;
18 
19 
20 import android.app.Activity;
21 import android.app.Fragment;
22 import android.app.FragmentManager;
23 import android.app.FragmentTransaction;
24 import android.content.SharedPreferences;
25 import android.os.Bundle;
26 import android.text.format.Time;
27 import android.util.Log;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.widget.AbsListView;
32 import android.widget.AbsListView.OnScrollListener;
33 import android.widget.Adapter;
34 import android.widget.HeaderViewListAdapter;
35 
36 import com.android.calendar.CalendarController;
37 import com.android.calendar.CalendarController.EventInfo;
38 import com.android.calendar.CalendarController.EventType;
39 import com.android.calendar.CalendarController.ViewType;
40 import com.android.calendar.EventInfoFragment;
41 import com.android.calendar.GeneralPreferences;
42 import com.android.calendar.R;
43 import com.android.calendar.StickyHeaderListView;
44 import com.android.calendar.StickyHeaderListView.HeaderIndexer;
45 import com.android.calendar.Utils;
46 
47 
48 public 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     private boolean mOnAttachAllDay = false;
70 
71     // Tracks the time of the top visible view in order to send UPDATE_TITLE messages to the action
72     // bar.
73     int  mJulianDayOnTop = -1;
74 
75     private Runnable mTZUpdater = new Runnable() {
76         @Override
77         public void run() {
78             mTimeZone = Utils.getTimeZone(getActivity(), this);
79             mTime.switchTimezone(mTimeZone);
80         }
81     };
82 
AgendaFragment()83     public AgendaFragment() {
84         this(0, false);
85     }
86 
87 
88     // timeMillis - time of first event to show
89     // usedForSearch - indicates if this fragment is used in the search fragment
AgendaFragment(long timeMillis, boolean usedForSearch)90     public AgendaFragment(long timeMillis, boolean usedForSearch) {
91         mInitialTimeMillis = timeMillis;
92         mTime = new Time();
93         if (mInitialTimeMillis == 0) {
94             mTime.setToNow();
95         } else {
96             mTime.set(mInitialTimeMillis);
97         }
98         mUsedForSearch = usedForSearch;
99     }
100 
101     @Override
onAttach(Activity activity)102     public void onAttach(Activity activity) {
103         super.onAttach(activity);
104         mTimeZone = Utils.getTimeZone(activity, mTZUpdater);
105         mTime.switchTimezone(mTimeZone);
106         mActivity = activity;
107         if (mOnAttachedInfo != null) {
108             showEventInfo(mOnAttachedInfo, mOnAttachAllDay);
109             mOnAttachedInfo = null;
110         }
111     }
112 
113     @Override
onCreate(Bundle icicle)114     public void onCreate(Bundle icicle) {
115         super.onCreate(icicle);
116         mController = CalendarController.getInstance(mActivity);
117         mShowEventDetailsWithAgenda =
118             Utils.getConfigBool(mActivity, R.bool.show_event_details_with_agenda);
119         mIsTabletConfig =
120             Utils.getConfigBool(mActivity, R.bool.tablet_config);
121         if (icicle != null) {
122             long prevTime = icicle.getLong(BUNDLE_KEY_RESTORE_TIME, -1);
123             if (prevTime != -1) {
124                 mTime.set(prevTime);
125                 if (DEBUG) {
126                     Log.d(TAG, "Restoring time to " + mTime.toString());
127                 }
128             }
129         }
130     }
131 
132     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)133     public View onCreateView(LayoutInflater inflater, ViewGroup container,
134             Bundle savedInstanceState) {
135 
136 
137         View v = inflater.inflate(R.layout.agenda_fragment, null);
138 
139         mAgendaListView = (AgendaListView)v.findViewById(R.id.agenda_events_list);
140         mAgendaListView.setClickable(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
onResume()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
onSaveInstanceState(Bundle outState)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      */
removeFragments(FragmentManager fragmentManager)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
onPause()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 
goTo(EventInfo event, boolean animate)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         AgendaAdapter.ViewHolder vh = mAgendaListView.getSelectedViewHolder();
269         showEventInfo(event, vh != null ? vh.allDay : false);
270     }
271 
search(String query, Time time)272     private void search(String query, Time time) {
273         mQuery = query;
274         if (time != null) {
275             mTime.set(time);
276         }
277         if (mAgendaListView == null) {
278             // The view hasn't been set yet. Just return.
279             return;
280         }
281         mAgendaListView.goTo(time, -1, mQuery, true);
282     }
283 
284     @Override
eventsChanged()285     public void eventsChanged() {
286         if (mAgendaListView != null) {
287             mAgendaListView.refresh(true);
288         }
289     }
290 
291     @Override
getSupportedEventTypes()292     public long getSupportedEventTypes() {
293         return EventType.GO_TO | EventType.EVENTS_CHANGED | ((mUsedForSearch)?EventType.SEARCH:0);
294     }
295 
296     @Override
handleEvent(EventInfo event)297     public void handleEvent(EventInfo event) {
298         if (event.eventType == EventType.GO_TO) {
299             // TODO support a range of time
300             // TODO support event_id
301             // TODO figure out the animate bit
302             goTo(event, true);
303         } else if (event.eventType == EventType.SEARCH) {
304             search(event.query, event.startTime);
305         } else if (event.eventType == EventType.EVENTS_CHANGED) {
306             eventsChanged();
307         }
308     }
309 
310 
311     // Shows the selected event in the Agenda view
showEventInfo(EventInfo event, boolean allDay)312     private void showEventInfo(EventInfo event, boolean allDay) {
313 
314         // Ignore unknown events
315         if (event.id == -1) {
316             Log.e(TAG, "showEventInfo, event ID = " + event.id);
317             return;
318         }
319 
320         // Create a fragment to show the event to the side of the agenda list
321         if (mShowEventDetailsWithAgenda) {
322             FragmentManager fragmentManager = getFragmentManager();
323             if (fragmentManager == null) {
324                 // Got a goto event before the fragment finished attaching,
325                 // stash the event and handle it later.
326                 mOnAttachedInfo = event;
327                 mOnAttachAllDay = allDay;
328                 return;
329             }
330             FragmentTransaction ft = fragmentManager.beginTransaction();
331             int response = CalendarController.ATTENDEE_NO_RESPONSE;
332             if (event.eventType == EventType.VIEW_EVENT
333                     || event.eventType == EventType.EDIT_EVENT) {
334                 response = (int) event.extraLong;
335             }
336 
337             if (allDay) {
338                 event.startTime.timezone = Time.TIMEZONE_UTC;
339                 event.endTime.timezone = Time.TIMEZONE_UTC;
340             }
341 
342             mEventFragment = new EventInfoFragment(mActivity, event.id,
343                     event.startTime.toMillis(true), event.endTime.toMillis(true),
344                     response, false);
345             ft.replace(R.id.agenda_event_info, mEventFragment);
346             mController.registerEventHandler(R.id.agenda_event_info,
347                     mEventFragment);
348             ft.commit();
349         }
350 //        else {
351 //            Intent intent = new Intent(Intent.ACTION_VIEW);
352 //            Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, event.id);
353 //            intent.setData(eventUri);
354 //            intent.setClass(getActivity(), AllInOneActivity.class);
355 //            intent.putExtra(EVENT_BEGIN_TIME, event.startTime != null ? event.startTime
356 //                    .toMillis(true) : -1);
357 //            intent.putExtra(EVENT_END_TIME, event.endTime != null ? event.endTime.toMillis(true)
358 //                    : -1);
359 //            startActivity(intent);
360 //        }
361     }
362 
363     // OnScrollListener implementation to update the date on the pull-down menu of the app
364 
onScrollStateChanged(AbsListView view, int scrollState)365     public void onScrollStateChanged(AbsListView view, int scrollState) {
366         // Do nothing
367     }
368 
369     // Gets the time of the first visible view. If it is a new time, send a message to update
370     // the time on the ActionBar
371     @Override
onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)372     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
373             int totalItemCount) {
374         int julianDay = mAgendaListView.getJulianDayFromPosition(firstVisibleItem
375                 - mAgendaListView.getHeaderViewsCount());
376         // On error - leave the old view
377         if (julianDay == 0) {
378             return;
379         }
380         // If the day changed, update the ActionBar
381         if (mJulianDayOnTop != julianDay) {
382             mJulianDayOnTop = julianDay;
383             Time t = new Time(mTimeZone);
384             t.setJulianDay(mJulianDayOnTop);
385             mController.setTime(t.toMillis(true));
386             // Cannot sent a message that eventually may change the layout of the views
387             // so instead post a runnable that will run when the layout is done
388             if (!mIsTabletConfig) {
389                 view.post(new Runnable() {
390                     @Override
391                     public void run() {
392                         Time t = new Time(mTimeZone);
393                         t.setJulianDay(mJulianDayOnTop);
394                         mController.sendEvent(this, EventType.UPDATE_TITLE, t, t, null, -1,
395                                 ViewType.CURRENT, 0, null, null);
396                     }
397                 });
398             }
399         }
400     }
401 }
402