DeskClock.java revision 6fc34de781d8fece646b16dc289bbf4c0b01fde4
1/*
2 * Copyright (C) 2009 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.deskclock;
18
19import android.app.ActionBar;
20import android.app.ActionBar.Tab;
21import android.app.Activity;
22import android.app.Fragment;
23import android.app.FragmentTransaction;
24import android.content.Context;
25import android.content.Intent;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.Message;
29import android.support.v13.app.FragmentStatePagerAdapter;
30import android.support.v4.view.ViewPager;
31import android.util.Log;
32import android.view.Menu;
33import android.view.MenuItem;
34import android.view.View;
35import android.view.animation.AnimationUtils;
36import android.widget.PopupMenu;
37import android.widget.Toast;
38
39import com.android.deskclock.timer.TimerFragment;
40
41import java.util.ArrayList;
42
43/**
44 * DeskClock clock view for desk docks.
45 */
46public class DeskClock extends Activity {
47    private static final boolean DEBUG = false;
48
49    private static final String LOG_TAG = "DeskClock";
50
51    // Alarm action for midnight (so we can update the date display).
52    private static final String KEY_SELECTED_TAB = "selected_tab";
53    private static final String KEY_CLOCK_STATE = "clock_state";
54
55    private ActionBar mActionBar;
56    private Tab mTimerTab;
57    private Tab mClockTab;
58    private Tab mStopwatchTab;
59
60    ViewPager mViewPager;
61    TabsAdapter mTabsAdapter;
62
63    private static final int TIMER_TAB_INDEX = 0;
64    private static final int CLOCK_TAB_INDEX = 1;
65    private static final int STOPWATCH_TAB_INDEX = 2;
66
67    private int mSelectedTab;
68    private final boolean mDimmed = false;
69
70    private int mClockState = CLOCK_NORMAL;
71    private static final int CLOCK_NORMAL = 0;
72    private static final int CLOCK_LIGHTS_OUT = 1;
73    private static final int CLOCK_DIMMED = 2;
74
75
76
77    // Delay before hiding the action bar and buttons
78    private static final long LIGHTSOUT_TIMEOUT = 10 * 1000; // 10 seconds
79    // Delay before dimming the screen
80    private static final long DIM_TIMEOUT = 10 * 1000; // 10 seconds
81
82    // Opacity of black layer between clock display and wallpaper.
83    private final float DIM_BEHIND_AMOUNT_NORMAL = 0.4f;
84    private final float DIM_BEHIND_AMOUNT_DIMMED = 0.8f; // higher contrast when display dimmed
85
86    private final int SCREEN_SAVER_TIMEOUT_MSG   = 0x2000;
87    private final int SCREEN_SAVER_MOVE_MSG      = 0x2001;
88    private final int DIM_TIMEOUT_MSG            = 0x2002;
89    private final int LIGHTSOUT_TIMEOUT_MSG      = 0x2003;
90    private final int BACK_TO_NORMAL_MSG         = 0x2004;
91
92
93
94    private final Handler mHandy = new Handler() {
95        @Override
96        public void handleMessage(Message m) {
97     /*       if (m.what == LIGHTSOUT_TIMEOUT_MSG) {
98                doLightsOut(true);
99                mClockState = CLOCK_LIGHTS_OUT;
100                // Only dim if clock fragment is visible
101                if (mViewPager.getCurrentItem() ==  CLOCK_TAB_INDEX) {
102                    scheduleDim();
103                }
104            }  else if (m.what == DIM_TIMEOUT_MSG) {
105                mClockState = CLOCK_DIMMED;
106                doDim(true);
107            } else if (m.what == BACK_TO_NORMAL_MSG){
108                // ignore user interaction and do not go back to normal if a button was clicked
109                DeskClockFragment f =
110                        (DeskClockFragment) mTabsAdapter.getFragment(mViewPager.getCurrentItem());
111                if (f != null && f.isButtonClicked()) {
112                    return;
113                }
114
115                int oldState = mClockState;
116                mClockState = CLOCK_NORMAL;
117
118                switch (oldState) {
119                    case CLOCK_LIGHTS_OUT:
120                        doLightsOut(false);
121                        break;
122                    case CLOCK_DIMMED:
123                        doLightsOut(false);
124                        doDim(true);
125                        break;
126                }
127            }*/
128        }
129    };
130
131    @Override
132    public void onNewIntent(Intent newIntent) {
133        super.onNewIntent(newIntent);
134        if (DEBUG) Log.d(LOG_TAG, "onNewIntent with intent: " + newIntent);
135
136        // update our intent so that we can consult it to determine whether or
137        // not the most recent launch was via a dock event
138        setIntent(newIntent);
139    }
140
141
142    private void initViews() {
143
144        if (mTabsAdapter == null) {
145            mViewPager = new ViewPager(this);
146            mViewPager.setId(R.id.desk_clock_pager);
147            mTabsAdapter = new TabsAdapter(this, mViewPager);
148            createTabs(mSelectedTab);
149        }
150        setContentView(mViewPager);
151        mActionBar.setSelectedNavigationItem(mSelectedTab);
152    }
153
154    private void createTabs(int selectedIndex) {
155        mActionBar = getActionBar();
156
157        mActionBar.setDisplayOptions(0);
158        if (mActionBar != null) {
159            mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
160            mTimerTab = mActionBar.newTab();
161            mTimerTab.setIcon(R.drawable.timer_tab);
162            mTabsAdapter.addTab(mTimerTab, TimerFragment.class,TIMER_TAB_INDEX);
163            mClockTab = mActionBar.newTab();
164            mClockTab.setIcon(R.drawable.clock_tab);
165            mTabsAdapter.addTab(mClockTab, ClockFragment.class,CLOCK_TAB_INDEX);
166            mStopwatchTab = mActionBar.newTab();
167            mStopwatchTab.setIcon(R.drawable.stopwatch_tab);
168            mTabsAdapter.addTab(mStopwatchTab, StopwatchFragment.class,STOPWATCH_TAB_INDEX);
169            mActionBar.setSelectedNavigationItem(selectedIndex);
170        }
171    }
172
173    @Override
174    protected void onCreate(Bundle icicle) {
175        super.onCreate(icicle);
176
177        mSelectedTab = CLOCK_TAB_INDEX;
178        if (icicle != null) {
179            mSelectedTab = icicle.getInt(KEY_SELECTED_TAB, CLOCK_TAB_INDEX);
180            mClockState = icicle.getInt(KEY_CLOCK_STATE, CLOCK_NORMAL);
181        }
182        initViews();
183    }
184
185    @Override
186    protected void onResume() {
187        super.onResume();
188        setClockState(false);
189    }
190
191    @Override
192    public void onPause() {
193        if (mTabsAdapter != null) {
194            mTabsAdapter.saveGlobalState();
195        }
196        super.onPause();
197    }
198
199    @Override
200    protected void onSaveInstanceState(Bundle outState) {
201        super.onSaveInstanceState(outState);
202        outState.putInt(KEY_SELECTED_TAB, mActionBar.getSelectedNavigationIndex());
203        outState.putInt(KEY_CLOCK_STATE, mClockState);
204        if (mTabsAdapter != null) {
205            mTabsAdapter.onSaveInstanceState(outState);
206        }
207    }
208
209    private void setClockState(boolean fade) {
210        doDim(fade);
211        switch(mClockState) {
212            case CLOCK_NORMAL:
213                doLightsOut(false);
214                break;
215            case CLOCK_LIGHTS_OUT:
216            case CLOCK_DIMMED:
217                doLightsOut(true);
218                break;
219            default:
220                break;
221        }
222    }
223
224    public void clockButtonsOnClick(View v) {
225        if (v == null)
226            return;
227        switch (v.getId()) {
228            case R.id.alarms_button:
229                startActivity(new Intent(this, AlarmClock.class));
230                break;
231            case R.id.cities_button:
232                Toast.makeText(this, "Not implemented yet", 2).show();
233                break;
234            case R.id.menu_button:
235                showMenu(v);
236                break;
237            default:
238                break;
239        }
240    }
241
242    private void showMenu(View v) {
243        PopupMenu popupMenu = new PopupMenu(this, v);
244        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener () {
245            @Override
246            public boolean onMenuItemClick(MenuItem item) {
247                switch (item.getItemId()) {
248                    case R.id.menu_item_settings:
249                        startActivity(new Intent(DeskClock.this, SettingsActivity.class));
250                        return true;
251                    case R.id.menu_item_help:
252                        startActivity(new Intent(DeskClock.this, HelpActivity.class));
253                        return true;
254                    default:
255                        break;
256                }
257                return true;
258            }
259        });
260        popupMenu.inflate(R.menu.desk_clock_menu);
261
262        Menu menu = popupMenu.getMenu();
263        MenuItem help = menu.findItem(R.id.menu_item_help);
264        if (help != null) {
265            Utils.prepareHelpMenuItem(this, help);
266        }
267        popupMenu.show();
268    }
269
270    private void scheduleLightsOut() {
271        mHandy.removeMessages(LIGHTSOUT_TIMEOUT_MSG);
272        mHandy.sendMessageDelayed(Message.obtain(mHandy, LIGHTSOUT_TIMEOUT_MSG), LIGHTSOUT_TIMEOUT);
273    }
274
275    public void doLightsOut(boolean state) {
276
277        if (state) {
278            mViewPager.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
279            mActionBar.hide();
280        } else {
281            mViewPager.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
282            mActionBar.show();
283        }
284
285        // in clock view show/hide the buttons at the bottom
286        if (mViewPager.getCurrentItem() ==  CLOCK_TAB_INDEX) {
287            Fragment f = mTabsAdapter.getFragment(CLOCK_TAB_INDEX);
288            if (f != null) {
289                ((ClockFragment)f).showButtons(!state);
290            }
291        }
292        if (!state) {
293            // Make sure dim will not start before lights out
294            mHandy.removeMessages(DIM_TIMEOUT_MSG);
295            scheduleLightsOut();
296        }
297    }
298
299    private void doDim(boolean fade) {
300        if (mClockState == CLOCK_DIMMED) {
301            mViewPager.startAnimation(
302                    AnimationUtils.loadAnimation(this, fade ? R.anim.dim : R.anim.dim_instant));
303        } else {
304            mViewPager.startAnimation(
305                    AnimationUtils.loadAnimation(this, fade ? R.anim.undim : R.anim.undim_instant));
306        }
307    }
308
309    private void scheduleDim() {
310        mHandy.removeMessages(DIM_TIMEOUT_MSG);
311        mHandy.sendMessageDelayed(Message.obtain(mHandy, DIM_TIMEOUT_MSG), DIM_TIMEOUT);
312    }
313
314    @Override
315    public void onUserInteraction() {
316        super.onUserInteraction();
317        mHandy.removeMessages(BACK_TO_NORMAL_MSG);
318        mHandy.sendMessage(Message.obtain(mHandy, BACK_TO_NORMAL_MSG));
319    }
320
321    /***
322     * Adapter for wrapping together the ActionBar's tab with the ViewPager
323     */
324
325    private class TabsAdapter extends FragmentStatePagerAdapter
326            implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
327
328        private static final String KEY_TAB_POSITION = "tab_position";
329
330        final class TabInfo {
331            private final Class<?> clss;
332            private final Bundle args;
333
334            TabInfo(Class<?> _class, int position) {
335                clss = _class;
336                args = new Bundle();
337                args.putInt(KEY_TAB_POSITION, position);
338            }
339
340            public int getPosition() {
341                return args.getInt(KEY_TAB_POSITION, 0);
342            }
343        }
344
345        private final ArrayList<TabInfo> mTabs = new ArrayList <TabInfo>();
346        private final DeskClockFragment [] mFragments = new DeskClockFragment[3];
347        ActionBar mMainActionBar;
348        Context mContext;
349        ViewPager mPager;
350
351        public TabsAdapter(Activity activity, ViewPager pager) {
352            super(activity.getFragmentManager());
353            mContext = activity;
354            mMainActionBar = activity.getActionBar();
355            mPager = pager;
356            mPager.setAdapter(this);
357            mPager.setOnPageChangeListener(this);
358        }
359
360        @Override
361        public Fragment getItem(int position) {
362            TabInfo info = mTabs.get(position);
363            DeskClockFragment f = (DeskClockFragment) Fragment.instantiate(
364                    mContext, info.clss.getName(), info.args);
365            f.setContext(mContext);
366            mFragments [position] = f;
367            return f;
368        }
369
370        public Fragment getFragment(int position) {
371            return mFragments [position];
372        }
373
374        @Override
375        public int getCount() {
376            return mTabs.size();
377        }
378
379        public void addTab(ActionBar.Tab tab, Class<?> clss, int position) {
380            TabInfo info = new TabInfo(clss, position);
381            tab.setTag(info);
382            tab.setTabListener(this);
383            mTabs.add(info);
384            mMainActionBar.addTab(tab);
385            notifyDataSetChanged();
386        }
387
388        @Override
389        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
390            // Do nothing
391        }
392
393        @Override
394        public void onPageSelected(int position) {
395            mMainActionBar.setSelectedNavigationItem(position);
396            onUserInteraction();
397        }
398
399        @Override
400        public void onPageScrollStateChanged(int state) {
401            // Do nothing
402        }
403
404        @Override
405        public void onTabReselected(Tab arg0, FragmentTransaction arg1) {
406            // Do nothing
407        }
408
409        @Override
410        public void onTabSelected(Tab tab, FragmentTransaction ft) {
411            TabInfo info = (TabInfo)tab.getTag();
412            mPager.setCurrentItem(info.getPosition());
413            onUserInteraction();
414        }
415
416        @Override
417        public void onTabUnselected(Tab arg0, FragmentTransaction arg1) {
418            // Do nothing
419
420        }
421
422        /***
423         *
424         * @param outState - can be null since it may be called from Activity's onPause
425         */
426        protected void onSaveInstanceState(Bundle outState) {
427            for (int i = 0; i < mFragments.length; i++) {
428                if (mFragments[i] != null) {
429                    mFragments[i].onSaveInstanceState(outState);
430                }
431            }
432        }
433
434        protected void saveGlobalState() {
435            for (int i = 0; i < mFragments.length; i++) {
436                if (mFragments[i] != null) {
437                    mFragments[i].saveGlobalState();
438                }
439            }
440        }
441
442    }
443}
444