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