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