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