1/*
2 * Copyright (C) 2010 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;
18
19import com.android.calendar.CalendarController.EventInfo;
20import com.android.calendar.CalendarController.EventType;
21
22import android.app.Fragment;
23import android.content.Context;
24import android.os.Bundle;
25import android.text.format.Time;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.view.ViewGroup;
29import android.view.ViewGroup.LayoutParams;
30import android.view.animation.Animation;
31import android.view.animation.AnimationUtils;
32import android.widget.ProgressBar;
33import android.widget.ViewSwitcher;
34import android.widget.ViewSwitcher.ViewFactory;
35
36/**
37 * This is the base class for Day and Week Activities.
38 */
39public class DayFragment extends Fragment implements CalendarController.EventHandler, ViewFactory {
40    /**
41     * The view id used for all the views we create. It's OK to have all child
42     * views have the same ID. This ID is used to pick which view receives
43     * focus when a view hierarchy is saved / restore
44     */
45    private static final int VIEW_ID = 1;
46
47    protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
48
49    protected ProgressBar mProgressBar;
50    protected ViewSwitcher mViewSwitcher;
51    protected Animation mInAnimationForward;
52    protected Animation mOutAnimationForward;
53    protected Animation mInAnimationBackward;
54    protected Animation mOutAnimationBackward;
55    EventLoader mEventLoader;
56
57    Time mSelectedDay = new Time();
58
59    private final Runnable mTZUpdater = new Runnable() {
60        @Override
61        public void run() {
62            if (!DayFragment.this.isAdded()) {
63                return;
64            }
65            String tz = Utils.getTimeZone(getActivity(), mTZUpdater);
66            mSelectedDay.timezone = tz;
67            mSelectedDay.normalize(true);
68        }
69    };
70
71    private int mNumDays;
72
73    public DayFragment() {
74        mSelectedDay.setToNow();
75    }
76
77    public DayFragment(long timeMillis, int numOfDays) {
78        mNumDays = numOfDays;
79        if (timeMillis == 0) {
80            mSelectedDay.setToNow();
81        } else {
82            mSelectedDay.set(timeMillis);
83        }
84    }
85
86    @Override
87    public void onCreate(Bundle icicle) {
88        super.onCreate(icicle);
89
90        Context context = getActivity();
91
92        mInAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_in);
93        mOutAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_out);
94        mInAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_in);
95        mOutAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_out);
96
97        mEventLoader = new EventLoader(context);
98    }
99
100    @Override
101    public View onCreateView(LayoutInflater inflater, ViewGroup container,
102            Bundle savedInstanceState) {
103        View v = inflater.inflate(R.layout.day_activity, null);
104
105        mViewSwitcher = (ViewSwitcher) v.findViewById(R.id.switcher);
106        mViewSwitcher.setFactory(this);
107        mViewSwitcher.getCurrentView().requestFocus();
108        ((DayView) mViewSwitcher.getCurrentView()).updateTitle();
109
110        return v;
111    }
112
113    public View makeView() {
114        mTZUpdater.run();
115        DayView view = new DayView(getActivity(), CalendarController
116                .getInstance(getActivity()), mViewSwitcher, mEventLoader, mNumDays);
117        view.setId(VIEW_ID);
118        view.setLayoutParams(new ViewSwitcher.LayoutParams(
119                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
120        view.setSelected(mSelectedDay, false, false);
121        return view;
122    }
123
124    @Override
125    public void onResume() {
126        super.onResume();
127        mEventLoader.startBackgroundThread();
128        mTZUpdater.run();
129        eventsChanged();
130        DayView view = (DayView) mViewSwitcher.getCurrentView();
131        view.handleOnResume();
132        view.restartCurrentTimeUpdates();
133
134        view = (DayView) mViewSwitcher.getNextView();
135        view.handleOnResume();
136        view.restartCurrentTimeUpdates();
137    }
138
139    @Override
140    public void onSaveInstanceState(Bundle outState) {
141        super.onSaveInstanceState(outState);
142
143        long time = getSelectedTimeInMillis();
144        if (time != -1) {
145            outState.putLong(BUNDLE_KEY_RESTORE_TIME, time);
146        }
147    }
148
149    @Override
150    public void onPause() {
151        super.onPause();
152        DayView view = (DayView) mViewSwitcher.getCurrentView();
153        view.cleanup();
154        view = (DayView) mViewSwitcher.getNextView();
155        view.cleanup();
156        mEventLoader.stopBackgroundThread();
157
158        // Stop events cross-fade animation
159        view.stopEventsAnimation();
160        ((DayView) mViewSwitcher.getNextView()).stopEventsAnimation();
161    }
162
163    void startProgressSpinner() {
164        // start the progress spinner
165        mProgressBar.setVisibility(View.VISIBLE);
166    }
167
168    void stopProgressSpinner() {
169        // stop the progress spinner
170        mProgressBar.setVisibility(View.GONE);
171    }
172
173    private void goTo(Time goToTime, boolean ignoreTime, boolean animateToday) {
174        if (mViewSwitcher == null) {
175            // The view hasn't been set yet. Just save the time and use it later.
176            mSelectedDay.set(goToTime);
177            return;
178        }
179
180        DayView currentView = (DayView) mViewSwitcher.getCurrentView();
181
182        // How does goTo time compared to what's already displaying?
183        int diff = currentView.compareToVisibleTimeRange(goToTime);
184
185        if (diff == 0) {
186            // In visible range. No need to switch view
187            currentView.setSelected(goToTime, ignoreTime, animateToday);
188        } else {
189            // Figure out which way to animate
190            if (diff > 0) {
191                mViewSwitcher.setInAnimation(mInAnimationForward);
192                mViewSwitcher.setOutAnimation(mOutAnimationForward);
193            } else {
194                mViewSwitcher.setInAnimation(mInAnimationBackward);
195                mViewSwitcher.setOutAnimation(mOutAnimationBackward);
196            }
197
198            DayView next = (DayView) mViewSwitcher.getNextView();
199            if (ignoreTime) {
200                next.setFirstVisibleHour(currentView.getFirstVisibleHour());
201            }
202
203            next.setSelected(goToTime, ignoreTime, animateToday);
204            next.reloadEvents();
205            mViewSwitcher.showNext();
206            next.requestFocus();
207            next.updateTitle();
208            next.restartCurrentTimeUpdates();
209        }
210    }
211
212    /**
213     * Returns the selected time in milliseconds. The milliseconds are measured
214     * in UTC milliseconds from the epoch and uniquely specifies any selectable
215     * time.
216     *
217     * @return the selected time in milliseconds
218     */
219    public long getSelectedTimeInMillis() {
220        if (mViewSwitcher == null) {
221            return -1;
222        }
223        DayView view = (DayView) mViewSwitcher.getCurrentView();
224        if (view == null) {
225            return -1;
226        }
227        return view.getSelectedTimeInMillis();
228    }
229
230    public void eventsChanged() {
231        if (mViewSwitcher == null) {
232            return;
233        }
234        DayView view = (DayView) mViewSwitcher.getCurrentView();
235        view.clearCachedEvents();
236        view.reloadEvents();
237
238        view = (DayView) mViewSwitcher.getNextView();
239        view.clearCachedEvents();
240    }
241
242    Event getSelectedEvent() {
243        DayView view = (DayView) mViewSwitcher.getCurrentView();
244        return view.getSelectedEvent();
245    }
246
247    boolean isEventSelected() {
248        DayView view = (DayView) mViewSwitcher.getCurrentView();
249        return view.isEventSelected();
250    }
251
252    Event getNewEvent() {
253        DayView view = (DayView) mViewSwitcher.getCurrentView();
254        return view.getNewEvent();
255    }
256
257    public DayView getNextView() {
258        return (DayView) mViewSwitcher.getNextView();
259    }
260
261    public long getSupportedEventTypes() {
262        return EventType.GO_TO | EventType.EVENTS_CHANGED;
263    }
264
265    public void handleEvent(EventInfo msg) {
266        if (msg.eventType == EventType.GO_TO) {
267// TODO support a range of time
268// TODO support event_id
269// TODO support select message
270            goTo(msg.selectedTime, (msg.extraLong & CalendarController.EXTRA_GOTO_DATE) != 0,
271                    (msg.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0);
272        } else if (msg.eventType == EventType.EVENTS_CHANGED) {
273            eventsChanged();
274        }
275    }
276}
277