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