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